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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
api_versionbuild_versionexcel_versionlinks
0v00.6.11310.00.5.3243{'relation': 'RequestLogs', 'href': 'http://bo...
\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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
fut_identifierintial_margineffective_date
0FutBund0024,525.252022-02-14T08:00:00Z
1FutBund0024,527.002022-02-17T08:00:00Z
2FutBund0024,523.002022-02-21T08:00:00Z
\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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
LusidInstrumentIdUnitsCostCurrencyCashType
0LUID_0000S7ZY10.001,350,000.00EUR<Not Classified>
1CCY_EUR45,252.5045,252.50EURFutures Initial Margin
\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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
LusidInstrumentIdUnitsCostCurrencyCashType
0LUID_0000S7ZY10.001,350,000.00EUR<Not Classified>
1CCY_EUR45,270.0045,270.00EURFutures Initial Margin
\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", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
LusidInstrumentIdUnitsCostCurrencyCashType
0LUID_0000S7ZY10.001,350,000.00EUR<Not Classified>
1CCY_EUR45,230.0045,230.00EURFutures Initial Margin
\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