SigmaField

Practice Trading

Simulate a trader's drawdowns

Medium+25 pointsAbout 35 minutesTrading

The brief

A systematic trading desk runs a strategy that makes $20,000 on about 53% of days and loses $20,000 on the rest. Risk management cuts any strategy whose P&L falls $400,000 below its high. Before giving it capital for another year, the head of trading wants to know how deep and how long its drawdowns get.

The last 60 days have been rough: the strategy is up only $40,000, and spent most of them below its high. Is that bad luck, or a sign that the edge has gone?

Your task

An equity curve is a list of cumulative P&L values, one per day, and its first point counts as a high. Its drawdown at a point is how far it is below the highest point so far, in dollars.

  1. Write drawdowns(equity): the drawdown at every point, as a NumPy array. Raise ValueError for an empty curve.
  2. Write max_drawdown(equity), the deepest drawdown, and longest_drawdown(equity), the longest run of consecutive points with a drawdown above 0.
  3. Write simulate_pnl(n_paths, n_days, p, step, seed=None): an array with one random walk per row and n_days + 1 columns. Each row starts at 0, then moves up by step with probability p and down by step otherwise. Use np.random.default_rng(seed).
  4. Write drawdown_risk(n_paths, n_days, p, step, limit, seed=None): simulate the paths and return a dict of statistics. median_depth and depth_95 are the median and 95th percentile of each path's deepest drawdown, and median_length and length_95 the same for its longest. prob_limit is the share of paths whose drawdown reaches limit.

simulate_pnl and drawdown_risk raise ValueError if p is outside [0,1][0, 1] or step is not positive. They also raise it if n_paths or n_days is not a whole number of at least 1, and drawdown_risk if limit is not positive.

The data

WIN_PROB, STEP, DAYS and LIMIT hold the strategy's model, a trading year of 252 days and the limit. RECENT_PNL holds the last 60 days of P&L, oldest first; the equity curve starts at 0 before the first of them. The record is synthetic.

Hint: Where to start

The P&L is lesson 5.11.1's random walk, with steps of $20,000. After nn days its mean is n(2p−1)n(2p - 1) steps and its standard deviation 4np(1−p)\sqrt{4np(1 - p)} steps, a check on simulate_pnl.

Hint: Whole arrays at once

The tests simulate 10,000 years, so avoid a Python loop over the paths. Lesson 5.11.1 builds thousands of walks at once, one per row, with cumsum(axis=1). np.maximum.accumulate(paths, axis=1) gives every path's running high in the same way.

Hint: An exact check

The drawdown is itself a Markov chain (lesson 5.11.2). At a high an up step leaves it at 0; otherwise each day moves it one step up or down. Stepping its distribution forward, as lesson 5.11.1 does for first passage, gives the exact chance of reaching the limit.