diff --git a/examples/use-cases/valuation/Futures Initial Margin using TimeVariant Properties.ipynb b/examples/use-cases/valuation/Futures Initial Margin using TimeVariant Properties.ipynb
new file mode 100644
index 00000000..7e1deacf
--- /dev/null
+++ b/examples/use-cases/valuation/Futures Initial Margin using TimeVariant Properties.ipynb
@@ -0,0 +1,1197 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "id": "b8352892",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ "\n",
+ " "
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "from lusidtools.jupyter_tools import toggle_code\n",
+ "\n",
+ "\"\"\"\n",
+ "Save externally calculated metrics and use them within the Valuation Engine\n",
+ "\n",
+ "Futures\n",
+ "TimeVariant Properties\n",
+ "Transactions\n",
+ "Holdings\n",
+ "----------\n",
+ "\"\"\"\n",
+ "\n",
+ "toggle_code(\"Toggle Docstring\")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "7de09a52",
+ "metadata": {},
+ "source": [
+ "# Initial Margin using Time Variant Properties\n",
+ "\n",
+ "In this notebook, we will demonstrate how you can process Futures Initial Margin. We will start with setting Initial Margin static data as Instrument time variant property \n",
+ "then booking Initial Margin requirement on transaction upsert and finally applying daily adjustment on Initial Margin Cash Account.\n",
+ "\n",
+ "\n",
+ "## Table of Contents\n",
+ "* [1. Create Portfolio](#1.-Create-Portfolio)\n",
+ "* [2. Create Properties](#2.-Create-Properties)\n",
+ "* [3. Create Future Instrument](#3.-Create-Future-Instrument)\n",
+ "* [4. Update Initial Margin on Future Instrument](#4.-Update-Initial-Margin-on-Future-Instrument)\n",
+ "* [5. Create Transaction Types](#5.-Create-Transaction-Types)\n",
+ "* [6. Load Transaction Data](#6.-Load-Transaction-Data)\n",
+ "* [7. Daily Initial Margin Adjustment](#7.-Daily-Initial-Margin-Adjustment)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "d0725982",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ ""
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " api_version | \n",
+ " build_version | \n",
+ " excel_version | \n",
+ " links | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " v0 | \n",
+ " 0.6.11310.0 | \n",
+ " 0.5.3243 | \n",
+ " {'relation': 'RequestLogs', 'href': 'http://bo... | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " api_version build_version excel_version \\\n",
+ "0 v0 0.6.11310.0 0.5.3243 \n",
+ "\n",
+ " links \n",
+ "0 {'relation': 'RequestLogs', 'href': 'http://bo... "
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# Use first block to import generic non-LUSID packages\n",
+ "import io\n",
+ "import os\n",
+ "import pandas as pd\n",
+ "pd.options.mode.chained_assignment = None\n",
+ "import numpy as np\n",
+ "import json\n",
+ "import pytz\n",
+ "from IPython.core.display import HTML\n",
+ "from datetime import datetime\n",
+ "\n",
+ "# Then import the key modules from the LUSID package (i.e. The LUSID SDK)\n",
+ "import lusid as lu\n",
+ "import lusid.api as la\n",
+ "import lusid.models as models\n",
+ "\n",
+ "# And use absolute imports to import key functions from Lusid-Python-Tools and other helper package\n",
+ "\n",
+ "from lusid.utilities import ApiClientFactory\n",
+ "from lusidjam import RefreshingToken\n",
+ "from lusidtools.cocoon.cocoon import load_from_data_frame\n",
+ "from lusidtools.pandas_utils.lusid_pandas import lusid_response_to_data_frame\n",
+ "from lusidtools.jupyter_tools import StopExecution\n",
+ "from lusidtools.cocoon.cocoon_printer import (\n",
+ " format_instruments_response,\n",
+ " format_portfolios_response,\n",
+ " format_transactions_response,\n",
+ ")\n",
+ "\n",
+ "# Set DataFrame display formats\n",
+ "pd.set_option(\"display.max_columns\", None)\n",
+ "pd.set_option(\"display.max_rows\", None)\n",
+ "pd.options.display.float_format = \"{:.2f}\".format\n",
+ "pd.set_option('display.float_format', lambda x: '{:,.2f}'.format(x))\n",
+ "display(HTML(\"\"))\n",
+ "\n",
+ "# Authenticate our user and create our API client\n",
+ "secrets_path = os.getenv(\"FBN_SECRETS_PATH\")\n",
+ "\n",
+ "if secrets_path is None:\n",
+ " secrets_path = os.path.join(os.path.dirname(os.getcwd()), \"secrets.json\")\n",
+ "\n",
+ "api_factory = ApiClientFactory(\n",
+ " token=RefreshingToken(),\n",
+ " api_secrets_filename=secrets_path,\n",
+ " app_name=\"LusidJupyterNotebook\",\n",
+ ")\n",
+ "\n",
+ "api_status = pd.DataFrame(\n",
+ " api_factory.build(lu.ApplicationMetadataApi).get_lusid_versions().to_dict()\n",
+ ")\n",
+ "\n",
+ "display(api_status)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "32f3c7b7",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "holdings_api = api_factory.build(lu.TransactionPortfoliosApi)\n",
+ "instruments_api = api_factory.build(lu.InstrumentsApi)\n",
+ "aggregation_api = api_factory.build(lu.AggregationApi)\n",
+ "configuration_recipe_api = api_factory.build(lu.ConfigurationRecipeApi)\n",
+ "property_definitions_api = api_factory.build(lu.PropertyDefinitionsApi)\n",
+ "transaction_portfolio_api = api_factory.build(lu.api.TransactionPortfoliosApi)\n",
+ "system_configuration_api = api_factory.build(lu.api.SystemConfigurationApi)\n",
+ "transaction_configuration_api = api_factory.build(lu.api.TransactionConfigurationApi)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "fd69fb7e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Define scopes, codes and property keys\n",
+ "scope = \"IMExampleNotebook\"\n",
+ "portfolio_code = \"FutureWithInitialMargin\"\n",
+ "recipe_code = \"futuresValuation\"\n",
+ "base_currency = \"EUR\"\n",
+ "sub_holding_key = f\"Transaction/{scope}/CashType\"\n",
+ "fut_initial_margin_key = f\"Instrument/{scope}/InitialMargin\"\n",
+ "txn_initial_margin_key = f\"Transaction/{scope}/InitialMargin\""
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "a5866588",
+ "metadata": {},
+ "source": [
+ "# 1. Create Portfolio"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "c9687494",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Could not create a portfolio with id 'FutureWithInitialMargin' because it already exists in scope 'IMExampleNotebook'.\n"
+ ]
+ }
+ ],
+ "source": [
+ "try:\n",
+ " transaction_portfolio_api.create_portfolio(\n",
+ " scope=scope,\n",
+ " create_transaction_portfolio_request=models.CreateTransactionPortfolioRequest(\n",
+ " display_name=portfolio_code,\n",
+ " code=portfolio_code,\n",
+ " base_currency=base_currency,\n",
+ " created=\"2010-01-01\",\n",
+ " sub_holding_keys=[sub_holding_key],\n",
+ " instrument_scopes=[scope]\n",
+ " ),\n",
+ " )\n",
+ "\n",
+ "except lu.ApiException as e:\n",
+ " if(e.status == 401):\n",
+ " print(e.reason)\n",
+ " else:\n",
+ " print(json.loads(e.body)[\"title\"])"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "0473c4f2",
+ "metadata": {},
+ "source": [
+ "# 2. Create Properties"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "59099705",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Error creating Property Definition 'Transaction/IMExampleNotebook/CashType' because it already exists.\n",
+ "Error creating Property Definition 'Transaction/IMExampleNotebook/InitialMargin' because it already exists.\n",
+ "Error creating Property Definition 'Instrument/IMExampleNotebook/InitialMargin' because it already exists.\n"
+ ]
+ }
+ ],
+ "source": [
+ "properties = [\n",
+ " (\"CashType\", \"string\", \"Transaction\",\"Perpetual\"),\n",
+ " (\"InitialMargin\", \"number\", \"Transaction\",\"Perpetual\"),\n",
+ " (\"InitialMargin\", \"number\", \"Instrument\",\"TimeVariant\")\n",
+ "]\n",
+ "\n",
+ "for property_code, dtype, domain, life_time in properties:\n",
+ " try:\n",
+ " property_definitions_api.create_property_definition(\n",
+ " create_property_definition_request=models.CreatePropertyDefinitionRequest(\n",
+ " domain=domain,\n",
+ " scope=scope,\n",
+ " code=property_code,\n",
+ " display_name=property_code,\n",
+ " data_type_id=models.ResourceId(code=dtype, scope=\"system\"),\n",
+ " life_time = life_time\n",
+ " )\n",
+ " )\n",
+ " except lu.ApiException as e:\n",
+ " print(json.loads(e.body)[\"title\"])"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "74ea2829",
+ "metadata": {},
+ "source": [
+ "# 3. Create Future Instrument"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "id": "13e855e6",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Define function that creates futures\n",
+ "def create_futures_contract(\n",
+ " dom_ccy,\n",
+ " contract_code,\n",
+ " contract_month,\n",
+ " contract_size,\n",
+ " convention,\n",
+ " country_id,\n",
+ " fut_name,\n",
+ " exchange_code,\n",
+ " exchange_name,\n",
+ " ticker_step,\n",
+ " unit_value,\n",
+ " ref_spot_price,\n",
+ " start_date,\n",
+ " maturity_date,\n",
+ " fut_identifier,\n",
+ " identifier_type\n",
+ "):\n",
+ " ctc = models.FuturesContractDetails(\n",
+ " dom_ccy=dom_ccy,\n",
+ " contract_code=contract_code,\n",
+ " contract_month=contract_month,\n",
+ " contract_size=contract_size,\n",
+ " convention=convention,\n",
+ " country=country_id,\n",
+ " description=fut_name,\n",
+ " exchange_code=exchange_code,\n",
+ " exchange_name=exchange_name,\n",
+ " ticker_step=ticker_step,\n",
+ " unit_value=unit_value,\n",
+ " )\n",
+ " futuredef = models.Future(\n",
+ " start_date=start_date,\n",
+ " maturity_date=maturity_date,\n",
+ " identifiers={},\n",
+ " contract_details=ctc,\n",
+ " contracts=1,\n",
+ " ref_spot_price=ref_spot_price,\n",
+ " underlying=models.ExoticInstrument(\n",
+ " instrument_format=models.InstrumentDefinitionFormat(\n",
+ " \"custom\", \"custom\", \"0.0.0\"\n",
+ " ),\n",
+ " content=\"{}\",\n",
+ " instrument_type=\"ExoticInstrument\",\n",
+ " ),\n",
+ " instrument_type=\"Future\",\n",
+ " )\n",
+ " # persist the instrument\n",
+ " futureDefinition = models.InstrumentDefinition(\n",
+ " name=fut_name,\n",
+ " identifiers={identifier_type: models.InstrumentIdValue(fut_identifier)},\n",
+ " definition=futuredef,\n",
+ " )\n",
+ " batchUpsertRequest = {fut_identifier: futureDefinition}\n",
+ " upsertResponse = instruments_api.upsert_instruments(request_body=batchUpsertRequest, scope=scope)\n",
+ " futLuid = upsertResponse.values[fut_identifier].lusid_instrument_id\n",
+ " print(futLuid)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "d10b90c9",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "LUID_0000S7ZY\n"
+ ]
+ }
+ ],
+ "source": [
+ "start_date = datetime(2021, 6, 8, tzinfo=pytz.utc)\n",
+ "maturity_date = datetime(2022, 3, 8, tzinfo=pytz.utc)\n",
+ "dom_ccy = \"EUR\"\n",
+ "contract_code = \"FGBL\" #bbg=OE\n",
+ "contract_month = \"H\"\n",
+ "contract_size = 100000\n",
+ "convention = \"ActualActual\"\n",
+ "country_id= \"DE\"\n",
+ "fut_name = \"EURO-BUND FUTURE Mar22\"\n",
+ "exchange_code = \"EUREX\"\n",
+ "exchange_name =\"Eurex\"\n",
+ "ticker_step = 0.01\n",
+ "unit_value = 10\n",
+ "ref_spot_price_val = 0\n",
+ "identifier = \"FutBund002\"\n",
+ "identifier_type = \"ClientInternal\"\n",
+ "\n",
+ "# Create Futures Contract function\n",
+ "create_futures_contract(\n",
+ " dom_ccy,\n",
+ " contract_code,\n",
+ " contract_month,\n",
+ " contract_size,\n",
+ " convention,\n",
+ " country_id,\n",
+ " fut_name,\n",
+ " exchange_code,\n",
+ " exchange_name,\n",
+ " ticker_step,\n",
+ " unit_value,\n",
+ " ref_spot_price_val,\n",
+ " start_date,\n",
+ " maturity_date,\n",
+ " identifier,\n",
+ " identifier_type\n",
+ ")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "e339d7e3",
+ "metadata": {},
+ "source": [
+ "# 4. Update Initial Margin on Future Instrument\n",
+ "Initial Margin is associated to Future Contract definition using time variant Instrument Properties."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "c2855a8d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Define function that updates futures initial margin\n",
+ "def upsert_initial_margin_property(\n",
+ " fut_identifier,\n",
+ " fut_identifier_type,\n",
+ " margin_value,\n",
+ " effective_from):\n",
+ " property_key = fut_initial_margin_key\n",
+ " property_request = models.UpsertInstrumentPropertyRequest(\n",
+ " identifier = fut_identifier,\n",
+ " identifier_type = fut_identifier_type,\n",
+ " properties =[\n",
+ " models.ModelProperty(\n",
+ " key=property_key,\n",
+ " value=models.PropertyValue(\n",
+ " metric_value=models.MetricValue(\n",
+ " value=margin_value\n",
+ " )\n",
+ " ),\n",
+ " effective_from=effective_from\n",
+ " )\n",
+ " ])\n",
+ " upsert_response = instruments_api.upsert_instruments_properties(scope = scope, \n",
+ " upsert_instrument_property_request = [property_request])\n",
+ " print(f\"{property_key} upserted at {upsert_response.as_at_date}.\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "id": "2f997cec",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " fut_identifier | \n",
+ " intial_margin | \n",
+ " effective_date | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " FutBund002 | \n",
+ " 4,525.25 | \n",
+ " 2022-02-14T08:00:00Z | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " FutBund002 | \n",
+ " 4,527.00 | \n",
+ " 2022-02-17T08:00:00Z | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " FutBund002 | \n",
+ " 4,523.00 | \n",
+ " 2022-02-21T08:00:00Z | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " fut_identifier intial_margin effective_date\n",
+ "0 FutBund002 4,525.25 2022-02-14T08:00:00Z\n",
+ "1 FutBund002 4,527.00 2022-02-17T08:00:00Z\n",
+ "2 FutBund002 4,523.00 2022-02-21T08:00:00Z"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# Read in initial margin data from file\n",
+ "futures_initial_margin_df = pd.read_csv(\"data/fut_initial_margin.csv\")\n",
+ "futures_initial_margin_df.head()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "id": "7a969341",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Instrument/IMExampleNotebook/InitialMargin upserted at 2023-05-16 16:28:11.464618+00:00.\n",
+ "Instrument/IMExampleNotebook/InitialMargin upserted at 2023-05-16 16:28:11.635033+00:00.\n",
+ "Instrument/IMExampleNotebook/InitialMargin upserted at 2023-05-16 16:28:11.792163+00:00.\n"
+ ]
+ }
+ ],
+ "source": [
+ "for _ , row in futures_initial_margin_df.iterrows():\n",
+ " upsert_initial_margin_property(row[\"fut_identifier\"], \n",
+ " identifier_type,\n",
+ " row[\"intial_margin\"],\n",
+ " row[\"effective_date\"])"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "513ac2f5",
+ "metadata": {},
+ "source": [
+ "# 5. Create Transaction Types"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "35a40408",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# New Side for Initial Margin Cash Movement\n",
+ "side_list = [\n",
+ " (\"IM-Side\",\n",
+ " models.SideDefinitionRequest(\n",
+ " security=\"Txn:SettlementCurrency\",\n",
+ " currency=\"Txn:SettlementCurrency\",\n",
+ " rate=\"SettledToPortfolioRate\",\n",
+ " units=txn_initial_margin_key,\n",
+ " amount=txn_initial_margin_key\n",
+ " ))\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "id": "d02a87cf",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "IM-Side already exists in LUSID\n",
+ "OpenContract has been created in LUSID\n",
+ "IMAdjustment has been created in LUSID\n"
+ ]
+ }
+ ],
+ "source": [
+ "# List of Transaction types\n",
+ "transaction_type_req = [\n",
+ " (\"OpenContract\", models.TransactionTypeRequest(\n",
+ " aliases=[\n",
+ " models.TransactionTypeAlias(\n",
+ " type=\"OpenContract\",\n",
+ " description=\"Open a long future contract\",\n",
+ " transaction_class=\"Futures\",\n",
+ " transaction_roles=\"LongLonger\",\n",
+ " )\n",
+ " ],\n",
+ " movements=[\n",
+ " models.TransactionTypeMovement(\n",
+ " movement_types=\"StockMovement\",\n",
+ " side=\"Side1\",\n",
+ " direction=1,\n",
+ " properties=None,\n",
+ " mappings=[],\n",
+ " name=\"Open contract\"\n",
+ " ),\n",
+ " models.TransactionTypeMovement(\n",
+ " movement_types=\"CashCommitment\",\n",
+ " side=\"IM-Side\",\n",
+ " direction=1,\n",
+ " properties=None,\n",
+ " mappings=[\n",
+ " models.TransactionPropertyMappingRequest(\n",
+ " property_key=sub_holding_key, set_to=\"Futures Initial Margin\")],\n",
+ " name=\"Initial Margin\"\n",
+ " )\n",
+ " ],\n",
+ " properties=None,\n",
+ " )),\n",
+ " \n",
+ " (\"IMAdjustment\", models.TransactionType(\n",
+ " aliases=[\n",
+ " models.TransactionTypeAlias(\n",
+ " type=\"IMAdjustment\",\n",
+ " description=\"Adjust Initial Margin\",\n",
+ " transaction_class=\"Futures\",\n",
+ " transaction_roles=\"AllRoles\",\n",
+ " )\n",
+ " ],\n",
+ " movements=[\n",
+ " models.TransactionTypeMovement(\n",
+ " movement_types=\"StockMovement\",\n",
+ " side=\"Side1\",\n",
+ " direction=1,\n",
+ " properties=None,\n",
+ " mappings=[],\n",
+ " name=\"Open contract\"\n",
+ " ),\n",
+ " models.TransactionTypeMovement(\n",
+ " movement_types=\"CashCommitment\",\n",
+ " side=\"IM-Side\",\n",
+ " direction=1,\n",
+ " properties=None,\n",
+ " mappings=[\n",
+ " models.TransactionPropertyMappingRequest(\n",
+ " property_key=sub_holding_key, set_to=\"Futures Initial Margin\")],\n",
+ " name=\"IM Increase/Decrease\"\n",
+ " )\n",
+ " ],\n",
+ " properties=None,\n",
+ " ))\n",
+ "]\n",
+ "\n",
+ "current_sides = [\n",
+ " side.side for side in system_configuration_api.list_configuration_transaction_types().side_definitions]\n",
+ "\n",
+ "for (side, side_req) in side_list:\n",
+ "\n",
+ " if side in list(current_sides):\n",
+ "\n",
+ " print(f\"{side} already exists in LUSID\")\n",
+ "\n",
+ " else:\n",
+ "\n",
+ " response = transaction_configuration_api.set_side_definition(\n",
+ " side=side, side_definition_request=side_req)\n",
+ "\n",
+ " print(f\"Side {side} has been created in LUSID\")\n",
+ "\n",
+ "for(type, type_req) in transaction_type_req:\n",
+ " transaction_configuration_api.set_transaction_type(\"default\", type, type_req)\n",
+ " print(f\"{type} has been created in LUSID\")"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "e22dcd0e",
+ "metadata": {},
+ "source": [
+ "# 6. Load Transaction Data\n",
+ "Create a Future Position on FutBund002 by upserting a new transaction."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "id": "b5b74d36",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def upsert_transaction(txn_id, txn_type,effective_date,txn_units,price,total_consideration,im_value):\n",
+ " transaction_requests=[\n",
+ " models.TransactionRequest(\n",
+ " transaction_id=txn_id,\n",
+ " type=txn_type,\n",
+ " instrument_identifiers={ \"Instrument/default/ClientInternal\": identifier },\n",
+ " transaction_date=effective_date,\n",
+ " settlement_date=effective_date,\n",
+ " units=txn_units,\n",
+ " transaction_price=models.TransactionPrice(\n",
+ " price=price, type=\"Price\"\n",
+ " ),\n",
+ " total_consideration=models.CurrencyAndAmount(\n",
+ " amount=total_consideration, currency=base_currency\n",
+ " ),\n",
+ " properties={txn_initial_margin_key: models.PerpetualProperty(\n",
+ " key=txn_initial_margin_key,\n",
+ " value=models.PropertyValue(metric_value=models.MetricValue(value=im_value)))}\n",
+ " )\n",
+ " ]\n",
+ "\n",
+ " transaction_portfolio_api.upsert_transactions(\n",
+ " scope=scope,\n",
+ " code=portfolio_code,\n",
+ " transaction_request=transaction_requests)\n",
+ " \n",
+ "# Open Future Position and compute Initial Margin requirement\n",
+ "txn_units = 10\n",
+ "effective_date = \"2022-02-14T09:00:00Z\"\n",
+ "# Get Initial Margin Property then compute the margin requirement\n",
+ "fut_initial_margin = instruments_api.get_instrument_properties(identifier_type = identifier_type, identifier = identifier, \n",
+ " effective_at = effective_date, scope= scope).properties.get(fut_initial_margin_key).value.metric_value.value\n",
+ "\n",
+ "txn_initial_margin = fut_initial_margin * txn_units\n",
+ "\n",
+ "upsert_transaction(\"txn_01\",\"OpenContract\",effective_date,txn_units,135,1350000,txn_initial_margin)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "a27c37ff",
+ "metadata": {},
+ "source": [
+ "# 7. Daily Initial Margin Adjustment\n",
+ "\n",
+ "Here we are looking at the daily adjustment needed to keep the Initial Margin Cash Account up to date. First, we compute the margin change between T and T-1 using the formula: $Units * (IM_{T} - IM_{T-1})$.\n",
+ "Then we book the margin adjustment by upserting a new transaction with transaction type **IMAdjustment**."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "id": "5fbc0e50",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def get_daily_fut_position(effective_date):\n",
+ " columns_to_rename = {\n",
+ " \"instrument_uid\": \"LusidInstrumentId\",\n",
+ " \"units\":\"Units\",\n",
+ " \"cost.amount\":\"Cost\",\n",
+ " \"currency\":\"Currency\",\n",
+ " f\"CashType({scope}-SubHoldingKeys)\": \"CashType\"\n",
+ " }\n",
+ " columns_to_drop = [\"instrument_scope\",\"instrument_scope\",\"SourcePortfolioScope(default-Properties)\",\"SourcePortfolioId(default-Properties)\",\"holding_type\",\n",
+ " \"settled_units\",\"cost.currency\",\"cost_portfolio_ccy.amount\",\"cost_portfolio_ccy.currency\",\"holding_type_name\"]\n",
+ "\n",
+ " holdings = lusid_response_to_data_frame(transaction_portfolio_api.get_holdings(scope,portfolio_code,effective_at = effective_date),rename_properties=True)\n",
+ " holdings = holdings.drop(columns = columns_to_drop)\n",
+ " result=holdings[columns_to_rename.keys()]\n",
+ " result.rename(columns=columns_to_rename, inplace=True)\n",
+ " return result"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "9aa8a115",
+ "metadata": {},
+ "source": [
+ "## Day 1\n",
+ "\n",
+ "On 2022-02-16T09:00:00Z, no Initial Margin change was recorded, no adjustment needed."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "id": "4e4fe906",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " LusidInstrumentId | \n",
+ " Units | \n",
+ " Cost | \n",
+ " Currency | \n",
+ " CashType | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " LUID_0000S7ZY | \n",
+ " 10.00 | \n",
+ " 1,350,000.00 | \n",
+ " EUR | \n",
+ " <Not Classified> | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " CCY_EUR | \n",
+ " 45,252.50 | \n",
+ " 45,252.50 | \n",
+ " EUR | \n",
+ " Futures Initial Margin | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " LusidInstrumentId Units Cost Currency CashType\n",
+ "0 LUID_0000S7ZY 10.00 1,350,000.00 EUR \n",
+ "1 CCY_EUR 45,252.50 45,252.50 EUR Futures Initial Margin"
+ ]
+ },
+ "execution_count": 16,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "effective_date_1 = \"2022-02-16T09:00:00Z\"\n",
+ "get_daily_fut_position(effective_date_1)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "id": "8c383a40",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def compute_initial_margin_adjustment(date_1, date_2):\n",
+ " \n",
+ " # Get Future Position on date_2\n",
+ " fut_units = transaction_portfolio_api.get_holdings(scope,portfolio_code,effective_at = date_2).values[0].units\n",
+ " # IM on date_1\n",
+ " fut_initial_margin_1 = instruments_api.get_instrument_properties(identifier_type = identifier_type, identifier = identifier, \n",
+ " effective_at = date_1, scope= scope).properties.get(fut_initial_margin_key).value.metric_value.value\n",
+ " # IM on date_2\n",
+ " fut_initial_margin_2 = instruments_api.get_instrument_properties(identifier_type = identifier_type, identifier = identifier, \n",
+ " effective_at = date_2, scope= scope).properties.get(fut_initial_margin_key).value.metric_value.value\n",
+ "\n",
+ " return fut_units * (fut_initial_margin_2 - fut_initial_margin_1)\n",
+ " "
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "a0865e73",
+ "metadata": {},
+ "source": [
+ "## Day 2\n",
+ "\n",
+ "On 2022-02-17T09:00:00Z, we get a new Initial Margin worth **4,527**. We compute the required adjustment and update IM account using CashType = 'Futures Initial Margin'"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "id": "d58f4b06",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "The required Initial Margin adjustment is 17.5 EUR\n"
+ ]
+ }
+ ],
+ "source": [
+ "effective_date_2 = \"2022-02-17T09:00:00Z\"\n",
+ "\n",
+ "adjustment_amount = compute_initial_margin_adjustment(effective_date_1, effective_date_2)\n",
+ "print(f\"The required Initial Margin adjustment is {adjustment_amount} EUR\")\n",
+ "\n",
+ "# Book IM adjustment\n",
+ "upsert_transaction(\"IM_01\",\"IMAdjustment\",effective_date_2,0,0,0,adjustment_amount)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "id": "4dd5436f",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " LusidInstrumentId | \n",
+ " Units | \n",
+ " Cost | \n",
+ " Currency | \n",
+ " CashType | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " LUID_0000S7ZY | \n",
+ " 10.00 | \n",
+ " 1,350,000.00 | \n",
+ " EUR | \n",
+ " <Not Classified> | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " CCY_EUR | \n",
+ " 45,270.00 | \n",
+ " 45,270.00 | \n",
+ " EUR | \n",
+ " Futures Initial Margin | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " LusidInstrumentId Units Cost Currency CashType\n",
+ "0 LUID_0000S7ZY 10.00 1,350,000.00 EUR \n",
+ "1 CCY_EUR 45,270.00 45,270.00 EUR Futures Initial Margin"
+ ]
+ },
+ "execution_count": 24,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "get_daily_fut_position(effective_date_2)"
+ ]
+ },
+ {
+ "attachments": {},
+ "cell_type": "markdown",
+ "id": "77ce254d",
+ "metadata": {},
+ "source": [
+ "## Day 3\n",
+ "\n",
+ "On 2022-02-21T09:00:00Z, we get a new Initial Margin worth **4,523**. We compute the required adjustment and update IM account using CashType = **'Futures Initial Margin'**"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "id": "80294ffc",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "The required Initial Margin adjustment is -40.0 EUR\n"
+ ]
+ }
+ ],
+ "source": [
+ "effective_date_3 = \"2022-02-21T09:00:00Z\"\n",
+ "\n",
+ "adjustment_amount = compute_initial_margin_adjustment(effective_date_2, effective_date_3)\n",
+ "print(f\"The required Initial Margin adjustment is {adjustment_amount} EUR\")\n",
+ "\n",
+ "# Book IM adjustment\n",
+ "upsert_transaction(\"IM_02\",\"IMAdjustment\",effective_date_3,0,0,0,adjustment_amount)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "id": "55625466",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " LusidInstrumentId | \n",
+ " Units | \n",
+ " Cost | \n",
+ " Currency | \n",
+ " CashType | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " LUID_0000S7ZY | \n",
+ " 10.00 | \n",
+ " 1,350,000.00 | \n",
+ " EUR | \n",
+ " <Not Classified> | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " CCY_EUR | \n",
+ " 45,230.00 | \n",
+ " 45,230.00 | \n",
+ " EUR | \n",
+ " Futures Initial Margin | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " LusidInstrumentId Units Cost Currency CashType\n",
+ "0 LUID_0000S7ZY 10.00 1,350,000.00 EUR \n",
+ "1 CCY_EUR 45,230.00 45,230.00 EUR Futures Initial Margin"
+ ]
+ },
+ "execution_count": 21,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "get_daily_fut_position(effective_date_3)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "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.9.7"
+ },
+ "toc": {
+ "base_numbering": 1,
+ "nav_menu": {},
+ "number_sections": false,
+ "sideBar": true,
+ "skip_h1_title": false,
+ "title_cell": "Table of Contents",
+ "title_sidebar": "Contents",
+ "toc_cell": false,
+ "toc_position": {},
+ "toc_section_display": true,
+ "toc_window_display": false
+ },
+ "varInspector": {
+ "cols": {
+ "lenName": 16,
+ "lenType": 16,
+ "lenVar": 40
+ },
+ "kernels_config": {
+ "python": {
+ "delete_cmd_postfix": "",
+ "delete_cmd_prefix": "del ",
+ "library": "var_list.py",
+ "varRefreshCmd": "print(var_dic_list())"
+ },
+ "r": {
+ "delete_cmd_postfix": ") ",
+ "delete_cmd_prefix": "rm(",
+ "library": "var_list.r",
+ "varRefreshCmd": "cat(var_dic_list()) "
+ }
+ },
+ "types_to_exclude": [
+ "module",
+ "function",
+ "builtin_function_or_method",
+ "instance",
+ "_Feature"
+ ],
+ "window_display": false
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/examples/use-cases/valuation/data/fut_initial_margin.csv b/examples/use-cases/valuation/data/fut_initial_margin.csv
new file mode 100644
index 00000000..fecb6be0
--- /dev/null
+++ b/examples/use-cases/valuation/data/fut_initial_margin.csv
@@ -0,0 +1,4 @@
+fut_identifier,intial_margin,effective_date
+FutBund002,4525.25,2022-02-14T08:00:00Z
+FutBund002,4527,2022-02-17T08:00:00Z
+FutBund002,4523,2022-02-21T08:00:00Z
\ No newline at end of file