Repository navigation
07: VaR and CVaR for the newsvendor, and the profit variables that lie - #6
Merged
Merged
Conversation
IE 5301 teaches seven newsvendor models in 2026 (02_Stochastic_Optimization_Newsvendor.ipynb); the library had four and stopped at the worst case. risk_measures.ipynb adds the other three: CVaR at 75% as an LP (the Rockafellar-Uryasev form), a dial weighting the mean against CVaR, and VaR at 75% as a MIP with one binary per scenario. 400 scenarios, the first 400 of newsvendor_demand.csv: every model is at most 4n + 2 = 1602 variables, inside the size-limited licence, so it runs on Colab with no key and its tests run in CI. With ALPHA 0.75 the tail is exactly 100 scenarios, so VaR (the 101st-worst profit) and CVaR (the mean of the worst 100) need no fractional-tail convention. tail_stats() refuses a sample that does not divide. The finding the notebook is built around, found while writing it: the VaR model's own profit variables put the order's tail CVaR at -456.49; scoring the same order against every scenario gives 3220.62. Excused scenarios' sales are free, so the solver leaves them anywhere. All 75 scenarios where the two differ are among the 100 excused. The first draft of a test here claimed "VaR abandons the tail" from exactly that misreading; the honest numbers say the VaR order beats the risk-neutral one on both tail measures, and the test now says so. solve_cvar and solve_var therefore return profits scored from the order, never the model's variables. Results (all four orders unique, checked by min/max of the order over each optimal set): risk-neutral 459.7374, which matches solve_stochastic; mix 391.1796; CVaR 306.6096; VaR 334.6840. The dial falls monotonically. Each order wins its own column (mean, VaR, CVaR); tests pin that as invariants, plus the orders. Gate: 201 passed with a full licence; 194 passed and 7 skipped (the existing newsvendor set) on the size-limited one; 19 notebooks and 13 seeds clean; ruff clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #5. Its base is
games/saddle-points. Merge #4, then #5, then this.Adds
notebooks/07_stochastic_and_newsvendor/risk_measures.ipynband package functionssolve_cvar,solve_varandtail_stats. These are the three newsvendor models IE 5301 teaches that the library didn't have:The notebook uses 400 scenarios, so every model fits the free Gurobi licence: it runs on Colab without a key, and its tests run in CI.
The lesson it's built around: if you read the VaR model's own profit variables, its worst-quarter average comes out at −456.49. Scoring the same order against every scenario gives 3,220.62. The 75 scenarios where the two differ are all ones the model excused, so their profit variables can sit anywhere the solver leaves them. My own first draft of a test made exactly that misreading; it's fixed, and the package now always re-scores profits from the order.
🤖 Generated with Claude Code