Divided by the Sum, Not the Average — JavaScript Bug Hunt
Modelled on JPMorgan Chase, 2012 (the "London Whale"): the bank's own task-force report into the Chief Investment Office's trading losses of more than $6…
- Language: JavaScript
- Layer: Backend
- Difficulty: Easy
- Concepts: Money, Math
- Modelled on: JPMorgan Chase · 2012
- Visible tests: a flat series has no volatility; a relative change divides by the average of the two rates
- Reward: 50 XP for a complete fix
Briefing
Modelled on JPMorgan Chase, 2012 (the "London Whale"): the bank's own task-force report into the Chief Investment Office's trading losses of more than $6 billion found that a new value-at-risk model was run through spreadsheets, and that one step — after subtracting an old rate from a new one — divided by their sum instead of their average. That muted the measured volatility by a factor of two and lowered the reported VaR.
risk.js is a reconstruction of that step: it turns a series of daily hazard-rate marks into relative changes, then into a volatility and a 95% VaR.
Fix relativeChange.
Bug report
BUG-VAR-CIO · Priority: Critical · Reported by: model review
relativeChange(oldRate, newRate) = (newRate - oldRate) / average(oldRate, newRate) where average(a, b) = (a + b) / 2.
- changes(rates): relativeChange for each consecutive pair
- volatility(rates): stats.round6(population stdev of changes(rates))
- valueAtRisk(position, rates): Math.round(stats.Z_95 volatility(rates) position)
Examples: relativeChange(0.02, 0.03) = 0.4; volatility([0.02, 0.03, 0.02, 0.03]) = 0.377124; valueAtRisk(1000000, that series) = 620369.
Observed: every volatility the model reports is exactly half the desk's own recalculation.
Logs
[var] book=synthetic-credit vol=0.188562 var95=310185
[model-review] independent recalculation: vol=0.377124 var95=620369The code as shipped
src/risk/risk.js (editable)
var stats = require("./stats");
// Relative change in a hazard rate between two daily marks.
function relativeChange(oldRate, newRate) {
return (newRate - oldRate) / (oldRate + newRate);
}
exports.relativeChange = relativeChange;
exports.changes = function (rates) {
var out = [];
for (var i = 1; i < rates.length; i++) out.push(relativeChange(rates[i - 1], rates[i]));
return out;
};
exports.volatility = function (rates) {
return stats.round6(stats.stdev(exports.changes(rates)));
};
exports.valueAtRisk = function (position, rates) {
return Math.round(stats.Z_95 * exports.volatility(rates) * position);
};
Read-only context: src/risk/stats.js.
Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More JavaScript bug hunts.