Customizing Model Inventory: Risk Tiering
This example shows how to customize the Model Inventory to hold information specific to your organization.
You can customize the model data entry and the model summary table. You can also add new filters to the Inventory Browser app to make it easy to find models with a particular value of a custom attribute.
This example uses a simple tree-based approach to Risk Tiering as an example. The model is from a paper by Mankatonia and Joshi (Measuring model risk: a practitioner’s approach, RMA Journal, 2013). This example along with other examples are also discussed in a paper by Kiritz, Ravitz and Levonian (Model risk tiering: an exploration of industry practices and principles, Journal of Risk Model Validation, 2019).
Add Custom Data to Inventory Browser
The inventory data is most likely stored outside of MATLAB®, for example, in a database. Various features of the Inventory Browser such as the model entry form do not access this external resource directly - rather, they interact with it through a client. Modelscape™ supports both database-backed and (for test use) in-memory clients.
Add new model-specific data to the Inventory Browser as references. To do this:
If necessary, create a new type for the reference.
Create the reference itself.
Associate the reference to a given model.
Create a new reference type called RiskTieringData
with the following attributes:
RiskPriceValueUse
: a string Unset, True or False denoting whether the model is used to measure risk, price, or value.CriticalUse
: a string Unset, True or False denoting whether the model is used for critical business decisions, regulatory reporting, or similar.Exposure
: a string Unset, High, Medium, or Low denoting the exposure level of the model.Override
: a string Unset, High, Medium, or Low denoting a risk tier level override.RiskTier
: a string Unset, High, Medium, or Low denoting the final risk tier which is worked out from the information above.
Construct a client with a model and this reference type, and attach a reference to this model. To learn more about how to do this, contact MathWorks Consulting Services.
Open an Inventory session with this client.
app = mrm.inventory.InventoryApp(client); app.open
Customize Model Data Entry
Customize the Inventory Browser model entry by adding new tabs next to the 'Details' included in the default view, and by changing the layout and contents of the 'Details' tab. Implement these customizations as subclasses of mrm.inventory.model.FormCustomization
. Include the subclasses in +
mrm
/+inventory/+custom/+model/
folder on the MATLAB path. Implement the following methods for each subclass.
The constructor must take two inputs: the parent
mrm.inventory.model.Form
object and the client carried by theForm
. If you want to assign these to theForm
andClient
properties, leave this to the base class constructor and not implement this at all.Use
populateCustomContents
(this)
to set up the additional tab and any controls such as drop-downs.onModelSet
(this)
must obtain the required references through the client and set these values to the controls. Note that the identifier of the model being displayed on the forms can be obtained from theGUIDEdit
property of the parent form.onSubmit
(this)
must read the values carried by the dropdowns and other controls and use the client to update the relevant references associated to the model.
Note that you can customize the 'Details' tab by modifying the layout grid, stored as the DetailsLayout
property, of the parent mrm.inventory.model.Form
object. Here are some examples of possible customizations.
Replace controls with new custom controls by hiding the existing control. To do this, use the property
Visible
and create a new control in the same location in the grid.Add new controls to the form by resizing the
DetailsLayout
grid.Reorganize controls by using
Layout.Row
properties.
Finally, you can overwrite the labels of the 'Details' tab controls in a customization class. See mrm.inventory.model.Form
for the names of properties defining the labels.
The following code snippet illustrates how you can apply these customizations to implement a risk tiering form. For the form layout, populateCustomContents
creates dropdowns for RiskPriceValueUse
, CriticalUse
, Exposure
and Override
, and a non-editable label to display the resulting RiskTier
. There is a button to recalculate the risk tier, as you want this to happen only once all the required inputs have been considered and set. Finally, the example shows a mechanism for displaying whether the risk tier stored in the inventory is in sync with the chosen inputs - if not, the comment '(Stale)' is added to the risk tier. The new tiering data is stored in the Inventory only when the 'Update' button is pressed.
classdef RiskTieringCustomTab < mrm.inventory.model.FormCustomization %Implements a custom tab for calculating the risk tier % Copyright 2021-2023 The MathWorks, Inc. properties (Access = private) % Base grid Grid(1,1) matlab.ui.container.GridLayout % Tree model controls UsageDD(1,1) matlab.ui.control.DropDown CriticalUseDD(1,1) matlab.ui.control.DropDown ExposureLevelDD(1,1) matlab.ui.control.DropDown OverrideDD(1,1) matlab.ui.control.DropDown CalculateButton(1,1) matlab.ui.control.Button RiskTierLabel(1,1) matlab.ui.control.Label % Convenience variable for setting the stale status of the tiering SavedTieringData struct % Cache the reference type for risk tiering data RiskTieringReferenceType end methods function this = RiskTieringCustomTab(form, client) this@mrm.inventory.model.FormCustomization(form, client); this.RiskTieringReferenceType = this.Client.getReferenceTypeByName("RiskTieringData"); end function populateCustomContent(this) parentTab = uitab(this.Form.TabGroup, ... 'Title', 'Risk tiering', ... 'Tag', 'risktiering_tab'); % Set up grid this.Grid = uigridlayout(parentTab, [6 2]); this.Grid.RowHeight = repmat(30, 1, 6); this.Grid.ColumnWidth = {'1x', '1x'}; % Row 1 uilabel(this.Grid, 'Text', 'Does the model measure risk, price or value?'); this.UsageDD = uidropdown(this.Grid, ... 'Items', ["Unset"; "True"; "False"], ... "ItemsData", ["Unset"; "True"; "False"], ... "ValueChangedFcn", @(~,~)this.setStaleStatus); % Row 2 uilabel(this.Grid, 'Text', 'Is the model usage critical?', ... 'Tooltip', 'Includes use for critical business decisions, regulatory purposes or financial reporting?'); this.CriticalUseDD = uidropdown(this.Grid, ... 'Items', ["Unset"; "True"; "False"], ... "ItemsData", ["Unset"; "True"; "False"], ... "ValueChangedFcn", @(~,~)this.setStaleStatus); % Row 3 uilabel(this.Grid, 'Text', 'Exposure'); this.ExposureLevelDD = uidropdown(this.Grid, ... 'Items', ["Unset"; "High"; "Medium"; "Low"], ... 'ItemsData', ["Unset"; "High"; "Medium"; "Low"], ... 'ValueChangedFcn', @(~,~)this.setStaleStatus); % Row 4 % Tier names 5-7 are 'Low (Stale)' etc, so don't include them in the drop-down. uilabel(this.Grid, 'Text', 'Override'); this.OverrideDD = uidropdown(this.Grid, ... 'Items', ["Unset"; "Low"; "Medium"; "High"], ... "ItemsData", ["Unset"; "Low"; "Medium"; "High"], ... "ValueChangedFcn", @(~,~)this.setStaleStatus); % Row 5 this.CalculateButton = uibutton(this.Grid, 'Text', 'Calculate'); this.CalculateButton.ButtonPushedFcn = @this.onCalculateRiskTier; this.CalculateButton.Layout.Row = 5; this.CalculateButton.Layout.Column = 2; % Row 6 uilabel(this.Grid, 'Text', 'Risk tier'); this.RiskTierLabel = uilabel(this.Grid, 'Text', ''); end function onModelSet(this) guid = this.Form.GUIDEdit.Value; tieringDataForThisModel = this.Client.getReferenceByModelAndType( ... guid, this.RiskTieringReferenceType.GUID); this.SavedTieringData = tieringDataForThisModel.Attributes; this.UsageDD.Value = this.SavedTieringData.RiskPriceValueUse; this.CriticalUseDD.Value = this.SavedTieringData.CriticalUse; this.ExposureLevelDD.Value = this.SavedTieringData.Exposure; this.OverrideDD.Value = this.SavedTieringData.Override; this.RiskTierLabel.Text = this.SavedTieringData.RiskTier; end function onSubmit(this) guid = this.Form.GUIDEdit.Value; data = containers.Map; data("RiskPriceValueUse") = this.UsageDD.Value; data("CriticalUse") = this.CriticalUseDD.Value; data("Exposure") = this.ExposureLevelDD.Value; data("Override") = this.OverrideDD.Value; data("RiskTier") = this.RiskTierLabel.Text; tieringReference = this.Client.getReferenceByModelAndType( ... guid, this.RiskTieringReferenceType.GUID); this.Client.updateReference(tieringReference.GUID, "Attributes", ... data); end end methods (Access = protected) function setStaleStatus(this) isStale = this.SavedTieringData.RiskPriceValueUse ~= this.UsageDD.Value || ... this.SavedTieringData.CriticalUse ~= this.CriticalUseDD.Value || ... this.SavedTieringData.Exposure ~= this.ExposureLevelDD.Value || ... this.SavedTieringData.Override ~= this.OverrideDD.Value; if isStale && ~contains(this.RiskTierLabel.Text, "Stale") && ... this.RiskTierLabel ~= "Unset" this.RiskTierLabel.Text = string(this.RiskTierLabel.Text) + " (Stale)"; elseif ~isStale && contains(this.RiskTierLabel.Text, "Stale") this.RiskTierLabel.Text = extractBefore(this.RiskTierLabel.Text, ... " (Stale)"); end end function onCalculateRiskTier(this, ~, ~) tieringInputs.riskpricevalueflag = this.UsageDD.Value; tieringInputs.criticalflag = this.CriticalUseDD.Value; tieringInputs.exposurelevel = this.ExposureLevelDD.Value; tieringInputs.override = this.OverrideDD.Value; riskTier = mrm.inventory.custom.riskTierTreeSimple(tieringInputs); this.RiskTierLabel.Text = riskTier; end end end
Customize Model Summary Table
Customize the summary table of model data shown in the Inventory Browser by omitting columns, including columns corresponding to the custom data set up in the previous two sections, and reordering any of the columns shown. Implement these customizations as a single subclass of mrm.inventory.model.TableCustomization
that must be in a +
mrm
/+inventory/+custom/+model
folder on the MATLAB path. The class must implement the following methods:
The constructor must accept a single input consisting of the user-visible headers for the default view of the model table. It must also set properties
ColumnVisible
,ColumnOrdering
andAllHeaders
.process(
this,
uit
,
modelIds
, client)
takes as its inputs theuitable
being customized and the ids of the model to be displayed, and performs the required customizations. Client is also supplied for looking up custom data.
The following example code illustrates how this can be done. The resulting table shows only the name and the id of each model from the base product model data, and the exposure level, risk tier and any possible override from the tiering data itself. These columns are also reordered to demonstrate this capability.
classdef TableCustomizationExample < mrm.inventory.model.TableCustomization %Example to illustrate addition, removal and reordering of summary table %column. % Copyright 2021-2023 The MathWorks, Inc. methods function this = TableCustomizationExample(parentHeaders) this.ExtraHeaders = ["Risk Tier", "RiskPrice", "Exposure", "Critical", "Tier override"]; this.AllHeaders = [parentHeaders, this.ExtraHeaders]; baseVisible = [true, true, false]; % 1-2 of visible columns riskTierVisible = [true, false, true, false, true]; % 3-5 of visible columns this.ColumnVisible = [baseVisible, riskTierVisible]; this.ColumnOrdering = [2 1 3 5 4]; % for visible columns only end function uit = process(this, uit, modelIds, client) arguments this uit matlab.ui.control.Table modelIds(1,:) string client end % Step 1: Read the risk tiering data for all the modelIds from % the client. tieringDataType = client.getReferenceTypeByName("RiskTieringData"); tieringData = arrayfun(@(id)client.getReferenceByModelAndType(id, ... tieringDataType.GUID), modelIds); % Step 2: Arrange this to extraModelTable table with columns % corresponding to this.ExtraHeaders [tiers, materialUseFlags, exposures, criticalUseFlags, overrides] = ... arrayfun(@(ref)readRiskTierData(ref), tieringData); extraModelData = table(tiers', materialUseFlags', exposures', ... criticalUseFlags', overrides', 'VariableNames', ... ["riskTier", "riskPriceValue", "exposure", "critical", "override"]); % Step 3: Concatenate this with uit.Data: uit.Data = [uit.Data, extraModelData]; uit.ColumnName = this.AllHeaders; % Step 4: Use the helper methods from TableCustomization base % to reset the visibility and the ordering of the columns. this.setVisibility(uit); this.setOrdering(uit); end end end function [tier, materialuseflag, exposure, criticaluseflag, override] = readRiskTierData(ref) tier = string(ref.Attributes.RiskTier); materialuseflag = string(ref.Attributes.RiskPriceValueUse); exposure = string(ref.Attributes.Exposure); criticaluseflag = string(ref.Attributes.CriticalUse); override = string(ref.Attributes.Override); end
Create Custom Filters
Inventory Browser is equipped with an interactive UI for creating filters to limit the list of models shown in the Models table. These filters make no distinction between data included in the default view and columns added as part of customization process. You can, for example, construct a filter to show only models with a ‘High’ risk tier.
Inventory Browser has two filters in the “Saved Filters” list of the filter editor: “Search by Name”, which allows you to filter by the model name, and “Create Custom Filter”, which shows a more complex filter, intended as a template for creating more complicated queries.
This section shows you how to create new filters and how to add them to the “Saved Filters” list.
To customize your own filters, implement new filters as subclasses of mrm.inventory.model.filter.Filter
Definition
with the following properties.
Name(
)
must carry a string that is to be displayed in the Filter dropdown - for example "Filter by Risk Tier"Serialization
must carry the default initialization of the filters as a JSON string
To understand the format of the serialization JSON, see mrm.inventory.model.filter.FilterByName
for simple (“Primitive”) filters that reference just a single column and see the default serialization in mrm.inventory.model.filter.CustomFilterTemplate for more complex (“Composite”) filters.
To make the filters visible, implement a function called modelFilters
in a +mrm/+inventory/+custom/+model/ folder on the MATLAB path. This function must take no inputs, and it must return a row array of all the filters you want to include in the Saved Filters list.
The code below illustrates this. Note that the modelFilters
output can include a mixture of custom filters and filters shipped with Modelscape itself.
function filters = modelFilters() %Example filter selection customization % Copyright 2022-2023 The MathWorks, Inc. filters = [ mrm.inventory.model.filter.FilterByName, ... mrm.inventory.custom.model.filter.FilterByRiskTier, ... mrm.inventory.model.filter.CustomFilterTemplate]; end
In the filter implementation, the variable name in uit.Data
may be different from the user-visible header shows in the Inventory - here riskTier
vs user-visible "Risk Tier". The implementation does not need to reside in any package folder, but it makes sense to have all customization code in a single location, so use +mrm/+inventory/+custom/+model/+filter here.
classdef FilterByRiskTier < mrm.inventory.model.filterDefinition % Example definition for filtering by risk tier in Modelscape Inventory % Copyright 2022-2023 The MathWorks, Inc. methods function this = FilterByRiskTier() this.name = "Filter by Risk Tier"; this.Serialization = ['{"type":"Primitive","header":"riskTier",', ... '"operation":"CONTAINS","value":"","parent":[],"id":"1"}']; end end end