monte_carlo#

  • Series: lecture-python-intro

  • File: lectures/monte_carlo.md

  • Audit date: 2026-08-26

  • Corpus snapshot: a12d17c0ef

  • Categories audited: writing, math, code, figures, links, admonitions (JAX out of scope)

  • Overall score: 7.7 / 10

  • Priority: LOW

Score breakdown#

Category

Score

One-line note

Writing

5.5/10

qe-writing-005 ×4; qe-writing-003 ×4; qe-writing-002 ×3, +2 more.

Math

5/10

qe-math-010 (proposed) ×10; qe-math-009 ×2.

Code

7.5/10

qe-code-001 ×4; qe-code-004 ×8.

JAX

out of scope

JAX rules target lecture-jax.

Figures

8/10

qe-fig-003 ×1; qe-fig-005 ×1; qe-fig-008 ×1.

References

N/A

no citations in this lecture.

Links

10/10

no mechanical violations detected.

Admonitions

10/10

no mechanical violations detected.

Issues#

Critical#

None found.

High severity#

  • [qe-code-004] — Use quantecon Timer context manager. Count: 8. Lines: 172, 229, 241, 610, 656, 667, 741, 777. Example: %%time.

  • [qe-math-010 (proposed)] — Blackboard \mathbb{P}, \mathbb{E}, \mathbb{V} with braces. Count: 10. Lines: 94, 101, 168, 273, 290, 341, 371, 382, 575. Example: missing braces: \mathbb E.

Medium severity#

  • [qe-code-001] (reviewer) — Follow PEP8 unless closer to mathematical notation. Count: 4. Lines: 191, 495, 526, 635. Example: 635-643 mis-indents a whole signature: def compute_call_price_vector( opens its parenthesis at column 29, so a visual indent is column 30, and the nine continuation lines sit at column 23 - the alignment of def compute_call_price( at 584, from which the function was copied. 526-531 is off in the other direction, at column 31 against a parenthesis at 29. 191 closes the compute_mean signature with     ): at four spaces, which is neither the indentation of the line that opens the construct nor the indentation of the parameters. And 495-499 (repeated at 693-697) pads before the assignment operator to align a block of constants - default_μ  = 0.0001, default_S0 = 10 - which is the “more than one space around an assignment operator to align it with another” that PEP8 lists under Pet Peeves. Two related items that are not spacing: return (S / n) at 198 wraps its return value in parentheses for nothing, and return_draws at 411 names an array of payoffs, np.maximum(S - K, 0), after returns.

  • [qe-fig-003] — No matplotlib embedded titles. Count: 1. Lines: 556. Example: .set_title.

  • [qe-math-009] (reviewer) — Choose simplicity in mathematical notation. Count: 2. Lines: 101, 273. Example: the mean and the variance of the same random variable, two displays apart, are written in two different notational systems: \mathbb E S at 94 and \mathop{\mathrm{Var}} S at 101, where the second hand-rolls an operator with \mathop{} that \operatorname{Var} (or \mathbb V, to match the expectation) gives for free. 273 mismatches its own delimiters inside a single line: \mathbb P\left\{G = 10^6 \right\} = \mathbb P\{G = 0\} - \left\{ \right\} on the left of the equals sign and plain \{ \} on the right, for contents that are one line tall either way. A third, smaller item: LN is set as maths italic at 87, 125 and 377, so the distribution’s name reads as a product \(L \cdot N\) where \mathrm{LN} would set it upright.

  • [qe-writing-002] (reviewer) — Keep writing clear, concise, and valuable. Count: 3. Lines: 80, 188, 467. Example: 188-198 restates 174-180 exactly. The loop body inside compute_mean is the same six lines as the bare loop above it, character for character; the function is called once at 206, never timed, and never used again, so the reader reads the same code twice and learns nothing from the second reading - the vectorized comparison at 219-243 is what the section is actually building to. 80-81 (“Either way, this discussion shows the importance of understanding the distribution of \(S\)”) closes a run of nine one-sentence paragraphs by restating their premise. And 464-467 says the same thing twice in two sentences and gets the second one wrong grammatically: “volatility (measured by \(\sigma\)) is not stationary” followed by “Instead it rather changes over time, sometimes high (like during the GFC) and sometimes low” - where “Instead it rather changes” needs an editor and “GFC” is used unexpanded in an introductory lecture with an international audience.

  • [qe-writing-003] (reviewer) — Maintain logical flow. Count: 4. Lines: 497, 668, 685, 696. Example: 668 breaks the exercise it is solving. monte_carlo_ex1 asks the reader to “write a faster version of this code using NumPy” (626); the solution defines compute_call_price_vector (634-652) and times it (655-657), then says “Now let’s try with larger \(M\) to get a more accurate calculation” (664) and calls compute_call_price(M=10_000_000) - the Python-loop version from 584, at a thousand times the sample size. The one cell meant to show the payoff of vectorising times the unvectorised function. 685 and 696 break the second exercise in two directions: 679 states “an underlying with spot price of $100 and a knockout barrier of $120” while the solution sets default_S0 = 10 (696) and puts 100 in default_K (698), so the exercise’s spot price is the solution’s strike and the solution’s spot price appears nowhere in the exercise; and 685 tells the reader to “use the dynamics defined in {eq}`s_mc_dyms`”, which is the constant-volatility model at 444-448, while the solution at 725-726 simulates the stochastic-volatility model of 476-484. 497 is quieter but undercuts a whole section: default_ν = 0.001 with \(h_0 = 0\) and \(\rho = 0.1\) confines \(h_t\) to about \([-0.001, 0.001]\), so \(\sigma_t = \exp(h_t)\) is constant to three decimal places and the “more realistic dynamics” that 458-486 spends thirty lines motivating is numerically the simple model it was introduced to replace - and nothing says so.

  • [qe-writing-005] (reviewer) — Use bold for definitions, italic for emphasis. Count: 4. Lines: 135, 258, 349, 681. Example: the lecture bolds four terms - risk-neutral price (284), expiry date, strike price and underlying (353-355) - and leaves every other term it defines with no emphasis at all, including the four the lecture is named after or built on. 135 defines the subject of the lecture in plain text: “This is the Monte Carlo method, which runs as follows”. 258 defines risk-neutral pricing in plain text (“When we use risk-neutral pricing, we determine the price of a given asset according to its expected payoff”) even though the number it produces is bolded twenty-six lines later. 349 introduces the European call option, the object of two whole sections, unmarked. 681 defines the knockout in the exercise (“if the spot price ever moves above $120, the option ‘knocks out’ and the contract is null and void”) with quotation marks doing the work bold should do. There is also no italic emphasis anywhere in the 781 lines, so the file has no working distinction between the two forms - just four bolded terms and nothing else.

  • [qe-writing-007] (reviewer) — Use visual elements to enhance understanding. Count: 3. Lines: 93, 411, 704. Example: a lecture about the distribution of a random payoff draws exactly one figure (547-560), and it is the one figure that is not about a distribution. 83-103 turns entirely on the shape of the lognormal - 71-78 asks the reader to weigh a high mean against a high variance, and 93-103 gives both in closed form - with no density plot to make either concrete. 409-414 draws ten million payoffs into return_draws and reduces them to a single printed number; the histogram is one line away and would show the reader that the option price is the mean of a very skewed distribution, which is the whole reason Monte Carlo is being used. And monte_carlo_ex2 (676-778) is about a path crossing a level: one simulated path with ax.axhline(bp) would show a knockout happening, and the exercise instead relies on the reader believing that option_is_null is doing what 767 says. The one figure that exists (547-560) has no axis labels, no legend and no lw, and it stacks 50 paths and 50 log paths where the point of comparing them is never stated in the prose.

  • [qe-writing-008] — Remove excessive whitespace between words. Count: 2. Lines: 124, 368. Example: 2 spaces.

Low severity#

  • [qe-fig-005] — Descriptive figure names for cross-referencing. Count: 1. Lines: 547. Example: code-cell figure without mystnb figure metadata.

  • [qe-fig-008] — Use lw=2 for line charts. Count: 1. Lines: 555. Example: plot() without lw=.

Strengths#

  • The lecture is one question asked four times with the tool getting stronger at each pass - compute \(\mathbb E S\) when the distribution is known (83-103), when it is not (109-139), when the payoff is an option (347-414), and when the dynamics generate the distribution (418-611) - and line 105, “So far we have no need for a computer”, marks the exact point where the method becomes necessary rather than merely convenient.

  • Risk neutrality is introduced honestly rather than assumed. 283 concedes that “the definition of ‘fair’ is ambiguous”; 295-313 spends a whole subsection on the fact that the risk-neutral price ignores risk, invites the reader to notice they would probably not take the 50/50 bet (302-306), and then says plainly at 312 why the benchmark is still worth computing.

  • Discounting is derived before it is used: 321-325 makes the bank-account argument, 330-333 states the two rules, and 338-343 then applies them to the number already computed at 290 rather than folding \(\beta\) silently into a formula - so \(\beta^n \mathbb E G\) arrives as a consequence, not a definition.

  • Every speed claim in the lecture is measured. The pure-Python loop (172), the vectorized function (229) and the vectorized function at ten times the sample size (241) are each timed, so “Notice that this routine is much faster” (236) sits next to the numbers that justify it.

  • The barrier option is solved twice and the second solution explains itself: the loop version breaks out of the path as soon as the barrier is crossed (727-730), and the vectorized version carries the knockout forward in a boolean mask with the two comments a reader needs at 767 and 770 - “Mark all the options null where S_n > barrier price” and “mark payoff as 0 in the indices where options are null” - which is exactly the step that gets lost in a vectorized rewrite.

  • The proof that \(S_n\) is lognormal (440-455) is an induction in four one-sentence steps, taking the log substitution, the normality of \(s_1\), the extension to \(s_n\) and the exponentiation in turn - the right granularity for a first course, and it is what licenses the closed-form pricing at 377-385.

  • Probability events are written with braces after \(\mathbb P\) (273), satisfying the proposed qe-math-014 (proposed) convention.