forked from Netflix-Skunkworks/riskquant
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request Netflix-Skunkworks#20 from Netflix-Skunkworks/add_…
…sample_notebook sample interactive notebook
- Loading branch information
Showing
1 changed file
with
339 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,339 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Simple Risk Quantification\n", | ||
"\n", | ||
"Please see the Riskquant blog if you want detailed info on the simulation code: \n", | ||
"https://medium.com/@NetflixTechBlog/open-sourcing-riskquant-a-library-for-quantifying-risk-6720cc1e4968\n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Step 1:\n", | ||
"\n", | ||
"## Update the Loss Fields\n", | ||
"\n", | ||
"These are sample values only. Update these fields with values from your internal loss tables." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 1, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"# Dollar value of one hour of one employee\n", | ||
"EMPLOYEE_HOURLY_COST = 50\n", | ||
"\n", | ||
"# Lowest cost to respond to an incident\n", | ||
"LOW_RESPONSE_COST = 100\n", | ||
"# Highest cost to respond to an incident\n", | ||
"HIGH_RESPONSE_COST = 10000\n", | ||
"\n", | ||
"# Lowest cost of PII data loss\n", | ||
"LOW_PII_COST = 10000\n", | ||
"# Highest cost of PII data loss\n", | ||
"HIGH_PII_COST = 1000000\n", | ||
"\n", | ||
"# Lowest cost of PCI data loss\n", | ||
"LOW_PCI_COST = 100000\n", | ||
"# Highest cost of PII data loss\n", | ||
"HIGH_PCI_COST = 10000000\n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"metadata": {}, | ||
"source": [ | ||
"# Step 2\n", | ||
"\n", | ||
"## Run the notebook\n", | ||
"\n", | ||
" 1. Click Cell > Run All\n", | ||
" 2. Scroll down to the bottom of this notebook\n", | ||
" 3. Under **Risk Quantification** make selections based on your scenario, and click Simulate Risk" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 2, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"# If you have not yet installed riskquant, install it\n", | ||
"# %pip install --upgrade riskquant" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 3, | ||
"metadata": { | ||
"scrolled": false | ||
}, | ||
"outputs": [ | ||
{ | ||
"data": { | ||
"text/markdown": [ | ||
"# Risk Quantification\n", | ||
"\n", | ||
"**This is a starting point. Please work with your Risk team for a detailed Risk Quantification.**\n", | ||
"\n", | ||
"**This is only a simulation based on the loss information entered.**\n", | ||
"\n", | ||
"**Instructions:**\n", | ||
"- Select a Loss Event Frequency: The frequency within the next year that loss will materialize from a threat agent’s action.\n", | ||
"- Enter the number of employees involved in responding to the scenario.\n", | ||
"- Enter the number of hours each employee will spend responding to the scenario.\n", | ||
"- Select if the scenario includes PCI and/or PII data.\n", | ||
"- Click **Simulate Risk**" | ||
], | ||
"text/plain": [ | ||
"<IPython.core.display.Markdown object>" | ||
] | ||
}, | ||
"metadata": {}, | ||
"output_type": "display_data" | ||
}, | ||
{ | ||
"data": { | ||
"application/vnd.jupyter.widget-view+json": { | ||
"model_id": "54f168bdce2342ffbf663d5d0d0879e0", | ||
"version_major": 2, | ||
"version_minor": 0 | ||
}, | ||
"text/plain": [ | ||
"VBox(children=(Dropdown(description='Loss Event Frequency:', layout=Layout(width='500px'), options=('Once per …" | ||
] | ||
}, | ||
"metadata": {}, | ||
"output_type": "display_data" | ||
} | ||
], | ||
"source": [ | ||
"# You can change the code here but you shouldn't need to... \n", | ||
"# Cnce you've clicked Cell > Run all, scroll to the bottom of this cell for the interactive section\n", | ||
"from IPython.display import display, Markdown, clear_output\n", | ||
"import ipywidgets as widgets\n", | ||
"from matplotlib import pyplot as plt\n", | ||
"from matplotlib import ticker as mtick\n", | ||
"import numpy as np\n", | ||
"from riskquant import pertloss\n", | ||
"\n", | ||
"def loss_exceedance_curve(simulate,\n", | ||
" title=\"Aggregated Loss Exceedance\",\n", | ||
" xlim=[100000, 1000000000]):\n", | ||
" losses = np.array([np.percentile(simulate, x) for x in range(1, 100, 1)])\n", | ||
" percentiles = np.array([float(100 - x) / 100.0 for x in range(1, 100, 1)])\n", | ||
" _ = plt.figure()\n", | ||
" ax = plt.gca()\n", | ||
" ax.plot(losses, percentiles)\n", | ||
" plt.title(title)\n", | ||
" ax.set_xscale(\"log\")\n", | ||
" ax.set_ylim(0.0, percentiles[np.argmax(losses > 0.0)] + 0.05)\n", | ||
" ax.set_xlim(xlim[0], xlim[1])\n", | ||
" xtick = mtick.StrMethodFormatter('${x:,.0f}')\n", | ||
" ax.xaxis.set_major_formatter(xtick)\n", | ||
" ytick = mtick.StrMethodFormatter('{x:.000%}')\n", | ||
" ax.yaxis.set_major_formatter(ytick)\n", | ||
" plt.grid(which='both')\n", | ||
" plt.show()\n", | ||
"\n", | ||
"# Frequency widgets\n", | ||
"frequency_menu = widgets.Dropdown(\n", | ||
" options=['Once per Day',\n", | ||
" 'Once per Week',\n", | ||
" 'Once per Month',\n", | ||
" 'Once per Three Months',\n", | ||
" 'Once per Year',\n", | ||
" 'Once per Two Years',\n", | ||
" 'Once per Ten Years',\n", | ||
" 'Once per One Hundred Years'],\n", | ||
" style={'description_width': '300px'},\n", | ||
" layout={'width': '500px'},\n", | ||
" description='Loss Event Frequency:')\n", | ||
"\n", | ||
"# Magnitude widgets\n", | ||
"emp = widgets.Text(value='0',description='Number of employees resolving scenario', \n", | ||
" style={'description_width': '300px'},\n", | ||
" layout={'width': '400px'} )\n", | ||
"emp_hours = widgets.Text(value='0',description='Number of hours each employee will spend',\n", | ||
" style={'description_width': '300px'},\n", | ||
" layout={'width': '400px'} )\n", | ||
"pci_checkbox = widgets.Checkbox(description='Scenario includes loss of PCI data',\n", | ||
" layout={'width': '400px'} )\n", | ||
"pii_checkbox = widgets.Checkbox(description='Scenario includes loss of PII data',\n", | ||
" layout={'width': '400px'})\n", | ||
"\n", | ||
"risk_button = widgets.Button(description='Simulate Risk')\n", | ||
"\n", | ||
"out = widgets.Output()\n", | ||
"\n", | ||
"def on_button_clicked(b):\n", | ||
" with out:\n", | ||
" clear_output() \n", | ||
" freq_input = frequency_menu.value\n", | ||
"\n", | ||
" if freq_input == 'Once per Day':\n", | ||
" minimum_frequency = 310\n", | ||
" maximum_frequency = 420\n", | ||
" most_likely_frequency = 365\n", | ||
" elif freq_input == 'Once per Week':\n", | ||
" minimum_frequency = 44\n", | ||
" maximum_frequency = 60\n", | ||
" most_likely_frequency = 52 \n", | ||
" elif freq_input == 'Once per Month':\n", | ||
" minimum_frequency = 8\n", | ||
" maximum_frequency = 16\n", | ||
" most_likely_frequency = 12 \n", | ||
" elif freq_input == 'Once per Three Months':\n", | ||
" minimum_frequency = 2\n", | ||
" maximum_frequency = 6\n", | ||
" most_likely_frequency = 4 \n", | ||
" elif freq_input == 'Once per Year':\n", | ||
" minimum_frequency = 0.5\n", | ||
" maximum_frequency = 2\n", | ||
" most_likely_frequency = 1 \n", | ||
" elif freq_input == 'Once per Two Years':\n", | ||
" minimum_frequency = 0.3\n", | ||
" maximum_frequency = 1\n", | ||
" most_likely_frequency = 0.5 \n", | ||
" elif freq_input == 'Once per Ten Years':\n", | ||
" minimum_frequency = 0.05\n", | ||
" maximum_frequency = 0.25\n", | ||
" most_likely_frequency = 0.1 \n", | ||
" elif freq_input == 'Once per One Hundred Years':\n", | ||
" minimum_frequency = 0.005\n", | ||
" maximum_frequency = 0.015\n", | ||
" most_likely_frequency = 0.01 \n", | ||
" else:\n", | ||
" minimum_frequency = 0.05\n", | ||
" maximum_frequency = 0.25\n", | ||
" most_likely_frequency = 0.1 \n", | ||
"\n", | ||
" if pci_checkbox.value and pii_checkbox.value:\n", | ||
" low_loss = LOW_PCI_COST + LOW_PII_COST \n", | ||
" high_loss = HIGH_PCI_COST + HIGH_PII_COST\n", | ||
" elif pci_checkbox.value and pii_checkbox.value == False:\n", | ||
" low_loss = LOW_PCI_COST\n", | ||
" high_loss = HIGH_PCI_COST \n", | ||
" elif pci_checkbox.value == False and pii_checkbox.value:\n", | ||
" low_loss = LOW_PII_COST\n", | ||
" high_loss = HIGH_PII_COST \n", | ||
" else:\n", | ||
" low_loss = LOW_RESPONSE_COST\n", | ||
" high_loss = HIGH_RESPONSE_COST \n", | ||
"\n", | ||
"\n", | ||
" try:\n", | ||
" emp_int = int(emp.value)\n", | ||
" except:\n", | ||
" print('Please enter an integer for number of employees')\n", | ||
"\n", | ||
" try:\n", | ||
" emp_hours_int = int(emp_hours.value)\n", | ||
" except:\n", | ||
" print('Please enter an integer for number of employees')\n", | ||
"\n", | ||
" emp_hour_cost = emp_hours_int * EMPLOYEE_HOURLY_COST\n", | ||
" emp_cost = emp_hour_cost * emp_int\n", | ||
"\n", | ||
" high_loss += emp_cost\n", | ||
"\n", | ||
" p = pertloss.PERTLoss(low_loss=low_loss, \n", | ||
" high_loss=high_loss, \n", | ||
" min_freq=minimum_frequency, \n", | ||
" max_freq=maximum_frequency, \n", | ||
" most_likely_freq=most_likely_frequency)\n", | ||
" simulate = p.simulate_years(50000)\n", | ||
" loss_summary = p.summarize_loss(simulate)\n", | ||
" annualized_loss_exposure = loss_summary['ninetieth_percentile'] * most_likely_frequency\n", | ||
" print('Annualized Loss Exposure (worst case):\\n')\n", | ||
" ale = round((most_likely_frequency * loss_summary['ninetieth_percentile']), -3)\n", | ||
" print('$', ale )\n", | ||
" print('\\nLoss Event Frequency:\\n')\n", | ||
" print(freq_input)\n", | ||
" print('\\nLoss summary:')\n", | ||
" print(loss_summary)\n", | ||
" loss_exceedance_curve(simulate)\n", | ||
"\n", | ||
"\n", | ||
"risk_button.on_click(on_button_clicked)\n", | ||
"info = Markdown(\"\"\"# Risk Quantification\n", | ||
"\n", | ||
"**This is a starting point. Please work with your Risk team for a detailed Risk Quantification.**\n", | ||
"\n", | ||
"**This is only a simulation based on the loss information entered.**\n", | ||
"\n", | ||
"**Instructions:**\n", | ||
"- Select a Loss Event Frequency: The frequency within the next year that loss will materialize from a threat agent’s action.\n", | ||
"- Enter the number of employees involved in responding to the scenario.\n", | ||
"- Enter the number of hours each employee will spend responding to the scenario.\n", | ||
"- Select if the scenario includes PCI and/or PII data.\n", | ||
"- Click **Simulate Risk**\"\"\")\n", | ||
"\n", | ||
"grapher = widgets.VBox([frequency_menu, emp, emp_hours, pci_checkbox, \n", | ||
" pii_checkbox, risk_button, out])\n", | ||
"display(info, grapher)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [] | ||
} | ||
], | ||
"metadata": { | ||
"hide_input": false, | ||
"kernelspec": { | ||
"display_name": "Python 3", | ||
"language": "python", | ||
"name": "python3" | ||
}, | ||
"language_info": { | ||
"codemirror_mode": { | ||
"name": "ipython", | ||
"version": 3 | ||
}, | ||
"file_extension": ".py", | ||
"mimetype": "text/x-python", | ||
"name": "python", | ||
"nbconvert_exporter": "python", | ||
"pygments_lexer": "ipython3", | ||
"version": "3.7.2" | ||
}, | ||
"toc": { | ||
"base_numbering": 1, | ||
"nav_menu": {}, | ||
"number_sections": true, | ||
"sideBar": true, | ||
"skip_h1_title": false, | ||
"title_cell": "Table of Contents", | ||
"title_sidebar": "Contents", | ||
"toc_cell": false, | ||
"toc_position": { | ||
"height": "calc(100% - 180px)", | ||
"left": "10px", | ||
"top": "150px", | ||
"width": "165px" | ||
}, | ||
"toc_section_display": true, | ||
"toc_window_display": true | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 4 | ||
} |