The objective of this project is to show that it is better to express regulatory requirements using executable expressions which all interested parties can share and run and test, rather than to express regulatory requirements as text documents which everyone has to interpret for themselves.
This page is a Jupyter notebook. It is a combination of a document and some live software which you can execute if you are running your own jupyter-notebook server. If you are not running a Jupyter server you can still read the document and see the code examples - you just can't run them.
If you see something wrong on this page or in the code, please create an issue in the GitHub project.
Governments would prefer to avoid another financial crisis like the one in 2008 and believe that making the big players operate in a more open and transparent way will help avoid another crash.
Markets in Financial Instruments Directive II (MiFID II) is an EU law which has market transparency as its key objective. The predecessor law, MiFID I, only looked at a part of what banking firms do. MiFID II aims to cover most mainstream activity.
Governments rely on regulators to make sure that their laws are being followed. For MiFID II the primary regulator is ESMA. ESMA have produced a number of Regulatory Technical Standard (RTS) documents which aim to explain what banking firms must do to comply with the MiFID II law.
One of the RTS documents, RTS 2, explains how different kinds of trading activity can be identified. Having a clear way to say what has been traded is an important part of making the markets more transparent.
Some kinds of trading activity are already pretty transparent, for example buying and selling shares in a public company. Trades of this kind are mostly done using a public exchange, such as the New York or London stock exchanges. The 'price' for a given stock is simply the amount of money paid in the most recent trade and this price is made public by the exchange so everyone can see what the latest price is. It is pretty easy to identify what has been traded because each stock has a identifier, e.g. 'AAPL' identifies Apple Inc. shares.
Not all trades happen on public exchanges. Many trades happen directly between two parties and these are known as over the counter (OTC) trades. Each OTC trade can be fine-tuned, for example setting payment dates and interest rates. The fine tuning of OTC trades makes it hard to give an identity to what has been traded, but this is where RTS 2 comes in.
The easiest way to understand what RTS 2 is all about is to use it to classify some trades, and you can do just that below.
It would be nice if ESMA published a working software implememtation of the RTS rules along with some test data so people can see exactly how the rules are supposed to work, and how reports are supposed to look. But ESMA don't do that. Each participant must somehow get an implementation of the RTS rules, either by writing it themselves or buying an implementation.
One market participant implemented the RTS rules themselves and have now released part of that implementation under an open source license, the BSD license, so anyone can see the implementaion and use it. This document forms a part of that release.
Hopefully this software will encourage ESMA to produce reference implementaions of their rules in future. They could even take this software as a starting point.
The software here is written in the Python programming language. Python was chosen because the language is ubiquitous, that is it can be used easily and immediately on most modern computers; everything from a Raspberry Pi to the largest of big data clusters.
The box below contains python code which runs the classification software. If you are just viewing this page then you won't be able to run the code, but if you start the page using your own local Jupyter notebook server then the code will really run if you select the box below and press control+enter. If you can run the code you might like to try changing the values of the attributes below (e.g. to_date) to see what happens.
In [1]:
# Import the RTS 2 module and the Python date & time tools module
import rts2_annex3
import datetime
# Create a simple Python object to represent a trade.
class SampleTrade(object):
pass
sample_trade = SampleTrade()
sample_trade.asset_class_name = 'Foreign Exchange Derivatives'
sample_trade.sub_asset_class_name= 'Deliverable FX options (DO)'
sample_trade.underlying_currency_pair = ('GBP~USD')
sample_trade.from_date = datetime.date(2017, 9, 13)
sample_trade.to_date = datetime.date(2017, 10, 12)
# Now classify the trade
sample_classification = rts2_annex3.class_root.classification_for(sample_trade)
# Lastly, display the classificaton
sample_classification.classification_dict()
Out[1]:
The classification is shown here as a Python dictionary, but one could imagine presenting this classification in many ways ... and this is a problem. What is the official accepted literal form of an RTS 2 classification? Nobody seems to know. So let's go with this dictionary for now.
Another point about the above representation is that it is very big, and the example is not one of the biggest! The reason for the size is that the text which appears is exactly as it appears in RTS 2. There is no obvious way to shorten the classification without inventing something, and that would open the door to arguments about what is right. This way, the classification links back to the RTS document in an extremely obvious, if verbose, way. No arguments.
To a large degree, the classification is simply repeating the information we gave our sample_trade object in the code above, but information has been checked and other information added.
This classification first confirms the identity of the RTS document the classification is based upon. The RTS rules may change over time, so it is important to know which version of the RTS a particular classification is based upon.
Next we see the Asset class and Sub-asset class, which is repeating just what we said above. When classifying a trade there are some things you just have to know. There will be some help on how to choose Asset classes and Sub-asset classes below.
Then we see something we didn't include in our trade object. The RTS 2 Annex 3 document defines a number of criteria for each kind of Sub-asset class. The Sub-asset class in this case has two criteria, and the classification included the description, the exact text, from the RTS document to explain what the criteria mean.
The values for the criteria do come from the values on our object, but some involve calculation. The currency pair criterion, criterion 1, is simply the name of underlying_currency_pair value we provided. Criterion 2 gets its value from date calculations which use the from and to dates we gave; the resulting value is a date bucket, bucket 2 in this case.
Because the classification is just a Python object we can change its implementation to render the classification in any way we please, or we can take the dictionary it currently produces and convert it to something else. Here, the classification above is shown as json:
In [2]:
print(sample_classification.as_json(indent=4))
To understand how the classification process works we need to look at what the RTS says.
The RTS 2 Annex 3 taxonomy is made up of Asset classes which get broken down into Sub-asset classes which are further broken down by combinations of criteria values.
Here is some code to list the names of elements of the taxonomy:
In [3]:
# The root of the taxonomy is rts2_annex3.class_root. Here we ask the
# root for the asset classes, and then ask each asset class for its name.
# The names are exactly the names of the Asset classes you'll see in the RTS document.
[asset_class.name for asset_class in rts2_annex3.class_root.asset_classes]
Out[3]:
In [4]:
# Each asset class is broken down into Sub-asset classes.
# So now we take the FX Derivatives asset class and display the names of
# its children, the sub-asset classes.
fx_asset_class = rts2_annex3.class_root.asset_class_by_name('Foreign Exchange Derivatives')
[sub_asset_class.name for sub_asset_class in fx_asset_class.children]
Out[4]:
In [5]:
# Each sub-asset class has a number of criteria.
# Here we ask the Deliverable FX Options sub-asset class to list its
# criteria:
fx_do_sub_asset_class = fx_asset_class.sub_asset_class_by_name('Deliverable FX options (DO)')
[criterion.description for criterion in fx_do_sub_asset_class.criteria]
Out[5]:
If you are running this notebook on a live Jupyter server then you can run the code below to display widgets which let you navigate the RTS 2 taxonomy.
You can select an asset class in a drop-down widget. This then populates the sub-asset classes drop-down widget for the selected asset class. Selecting a sub-asset class causes the criteria for that sub-asset class to be displayed.
Here is a screen shot of how the widgets look in action. In this example I have selected Energy Commodity Swaps which has seven criteria:
In [6]:
import rts2_annex3
import collections
import ipywidgets as widgets
from IPython.display import display
asset_classes = rts2_annex3.class_root.asset_classes
asset_class_dict = collections.OrderedDict([
(an_asset_class.name, an_asset_class)
for an_asset_class
in asset_classes])
asset_class_widget = widgets.Dropdown(
options=asset_class_dict,
description='Asset Classes:',
disabled=False,
)
def sub_asset_class_dict(asset_class):
return collections.OrderedDict([
(sub_asset_class.name, sub_asset_class)
for sub_asset_class
in asset_class.sub_asset_classes])
sub_asset_class_widget = widgets.Dropdown(
options=sub_asset_class_dict(asset_class_widget.value),
description='Sub-asset Classes:',
disabled=False,
)
criteria_vbox = widgets.VBox([])
def criteria_widgets(sub_asset_class):
# OK, in here I need to look up the criteria for the
# sub-asset class and build the widgets in rows of HBox es
return [widgets.Label(criterion.display(prefix=""))
for criterion
in sub_asset_class.criteria]
def asset_class_changed(change):
if change['type'] == 'change' and change['name'] == 'value':
selected_asset_class = change['new']
sub_asset_class_widget.options = sub_asset_class_dict(selected_asset_class)
def sub_asset_class_changed(change):
if change['type'] == 'change' and change['name'] == 'value':
selected_sub_asset_class = change['new']
criteria_vbox.children = criteria_widgets(selected_sub_asset_class)
asset_class_widget.observe(asset_class_changed)
sub_asset_class_widget.observe(sub_asset_class_changed)
display(asset_class_widget)
display(sub_asset_class_widget)
criteria_vbox.children = criteria_widgets(sub_asset_class_widget.value)
display(criteria_vbox)
The following code walks the RTS 2 Annex 3 taxonomy building up a string which presents the taxonomy as a tree, in the same kind of way that nested file folders on a computer could be shown as a tree.
The names are all trimmed to 50 characters just to force each item onto a single line.
In [7]:
import rts2_annex3
from IPython.display import display
max_name_length = 50
target_string = ''
root = rts2_annex3.class_root
target_string += 'Root\n'
for asset_class in root.asset_classes:
target_string += ' Asset class: "' + asset_class.name + '"\n'
for sub_asset_class in asset_class.children:
target_string += ' Sub-asset class: "' \
+ sub_asset_class.name[:max_name_length] \
+ '"\n'
for criterion in sub_asset_class.criteria:
target_string += ' Critrion: ' \
+ str(criterion.criterion_number) \
+ ' "' \
+ criterion.description[:max_name_length] \
+ '"\n'
print("\nDon't forget, all strings have been trimmed to {limit} characters! ... \n".format(
limit=max_name_length))
print(target_string)