{ "cells": [ { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "from gs_quant.session import Environment, GsSession\n", "# external users should substitute their client id and secret; please skip this step if using internal jupyterhub\n", "GsSession.use(Environment.PROD, client_id=None, client_secret=None)" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [ "# Factor Models\n", "\n", "The GS Quant `FactorRiskModel` class gives users the power to upload their own risk models to Marquee for seamless integration with the Marquee Portfolio Analytics and Plot Tool Pro suite. After uploading a custom `FactorRiskModel`, users can access their factor model data programmatically using GS Quant, visualize their factor risk model data with Plot Tool Pro, or run historical factor attribution analysis on equity portfolios through the lens of their uploaded factor risk model with GS Quant's `Portfolio` class." ], "metadata": { "collapsed": false } }, { "cell_type": "markdown", "source": [ "## Create a Factor Model\n", "\n", "Input fields to create the initial Factor Risk Model object\n", "\n", "| Attribute |Can be Modified |Description\n", "|-----------------|-------------------|-------------\n", "| id | No |Model id|\n", "| name | Yes |Name of model|\n", "| description | Yes |Longer description of model|\n", "| term | Yes |Term or horizon of model. One of: Long, Medium, Short|\n", "| coverage | Yes |Geographical coverage of assets within model universe. One of: Global, Region, Region Excluding Countries, Country|\n", "| vendor | Yes |Who creates the model|\n", "| version | Yes |Version of model|\n", "| identifier | No |Identifier used to upload the model's asset universe. One of: sedol, cusip, bcid, gsid|\n", "| entitlements | Yes |Who can manage, edit, and view the risk model|\n" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "from gs_quant.models.risk_model import FactorRiskModel, RiskModelCalendar, Term, CoverageType, UniverseIdentifier\n", "from gs_quant.entities.entitlements import Group, Entitlements, User, EntitlementBlock\n", "\n", "model_id = 'MY_MODEL'\n", "model_name = 'My Risk Model'\n", "description = 'My Custom Factor Risk Model'\n", "term = Term.Medium\n", "coverage = CoverageType.Country\n", "universe_identifier = UniverseIdentifier.sedol\n", "vendor = 'Goldman Sachs'\n", "\n", "users = User.get_many(emails=[\"first_user@email.com\", \"second_user@email.com\"])\n", "groups = Group.get_many(group_ids=[\"my_marquee_group1\",\"my_marquee_group_2\"])\n", "\n", "entitlements = Entitlements(\n", " admin=EntitlementBlock(users=users),\n", " edit=EntitlementBlock(users=users),\n", " upload=EntitlementBlock(users=users),\n", " query=EntitlementBlock(groups=groups, users=users),\n", " execute=EntitlementBlock(groups=groups, users=users),\n", " view=EntitlementBlock(groups=groups, users=users)\n", ")" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [ "Notes on entitlements for risk models:\n", "\n", "| Entitlement |Description\n", "|-----------------|-------------------\n", "| admin | Can edit this model's entitlements\n", "| edit | Can edit this model's metadata\n", "| upload | Can upload data to this model (raw data)\n", "| query | Can query this model's uploaded data (raw data)\n", "| execute | Can run risk reports with this model (derived data)\n", "| view | Can view risk reports run with this model\n", "\n" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } } }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "# create model with inputs\n", "\n", "model = FactorRiskModel(\n", " id_=model_id,\n", " name=model_name,\n", " description=description,\n", " coverage=coverage,\n", " term=term,\n", " universe_identifier=universe_identifier,\n", " vendor=vendor,\n", " entitlements=entitlements,\n", " version=1,\n", ")\n", "\n", "model.save()" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [ "## Upload a Calendar To Your Model\n", "The calendar associated with the Factor Risk Model contains the dates which the risk model should have posted data on to be considered \"complete.\" The calendar can go further back as well as forward in time than the data that is currently posted for the calendar, but there cannot be any gaps in the data posted to the risk model according to the calendar." ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "calendar = RiskModelCalendar([\n", " '2021-01-29', '2021-01-28', '2021-01-27', '2021-01-26', '2021-01-25', '2021-01-22', '2021-01-21',\n", " '2021-01-20', '2021-01-19', '2021-01-18', '2021-01-15', '2021-01-14', '2021-01-13', '2021-01-12',\n", " '2021-01-11', '2021-01-08', '2021-01-07', '2021-01-06', '2021-01-05', '2021-01-04', '2021-01-01'\n", "])\n", "\n", "model.upload_calendar(calendar)" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [ "## Upload Data To Your Model\n", "\n", "Once the calendar is posted for a model, we can start uploading data to it. We can supply data multiple ways:\n", "\n", "1. Upload total data one day at a time\n", "2. Upload partial data one day at a time\n", "\n", "For a complete day of data, we need three things, defined in `RiskModelData`\n", "1. Factor Data\n", " - factorId: Can be any string, but needs to map consistently to the same factor across every date\n", " - factorName: Can be any string, will be the display name of the factor, should be consistent across every date\n", " - factorCategoryId: Id of the category that the factor belongs to\n", " - factorCategory: Name of the category that the factor belongs to, will be the display name of the category (Style, Industry, Market, Currency, ect.)\n", " - factorReturn: Daily return of the factor in percent units\n", "2. Asset Data\n", " - universe: Array of assets in the universe\n", " - factorExposure: Array of dictionaries that map factorId to the factor exposure of that asset, corresponds to ordering of asset universe\n", " - specificRisk: Array of annualized specific risk in percent units, corresponds to ordering of asset universe (null values not allowed)\n", " - totalRisk: (optional) Array of total risk in percent units, corresponds to ordering of asset universe (null values not allowed)\n", " - historicalBeta: (optional) Array of historical beta, corresponds to ordering of asset universe (null values not allowed)\n", "3. Covariance Matrix\n", " - 2D array of the covariance between the factors in daily variance units. Ordering corresponds to the ordering of the Factor Data list in payload\n", "\n", "There are also some optional inputs:\n", "- Issuer Specific Covariance: The covariance attributed to two assets being issued by the same company (also known as Linked Specific Risk)\n", " - universeId1: Array of assets with issuer specific covariance to the asset in universeId2 at the same index. Each asset must also be present in the Asset Data universe\n", " - universeId1: Array of assets with issuer specific covariance to the asset in universeId1 at the same index. Each asset must also be present in the Asset Data universe\n", " - covariance: Array of the covariance between universeId1 and universeId2 at the same index. In daily variance units\n", "- Factor Portfolios: The weights of assets in the universe that combine to provide exposure of 1 to each factor\n", " - universe: Array of assets that make up the factor portfolios. Each asset must also be present in the Asset Data universe\n", " - portfolio: Array of:\n", " - factorId: Id of factor corresponding to the Factor Data factorIds\n", " - weights: Array of weights of each asset id, corresponding to the ordering of the universe given. Must have a weight for each asset in the universe, can have weights equal to 0 (null values not allowed)\n", "\n", "\n", "\n", "### Upload Full Data" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "data = {\n", " 'date': '2021-01-13', # Note: You can only upload to dates in your risk model's calendar\n", " 'assetData': {\n", " 'universe': ['B02V2Q0', '6560713', 'B3Q15X5', '0709954'],\n", " 'specificRisk': [12.09, 45.12, 3.09, 1.0],\n", " 'factorExposure': [\n", " {'1': 0.23, '2': 0.023},\n", " {'1': 0.023, '2': 2.09, '3': 0.3},\n", " {'1': 0.063, '2': 2.069, '3': 0.73},\n", " {'2': 0.067, '3': 0.93}\n", " ],\n", " 'historicalBeta': [0.12, 0.45, 1.2, 0.3]\n", " },\n", " 'factorData': [\n", " {\n", " 'factorId': '1',\n", " 'factorName': 'USD',\n", " 'factorCategory': 'Currency',\n", " 'factorCategoryId': 'CUR',\n", " 'factorReturn': 0.5\n", " },\n", " {\n", " 'factorId': '2',\n", " 'factorName': 'Technology',\n", " 'factorCategory': 'Industry',\n", " 'factorCategoryId': 'IND',\n", " 'factorReturn': 0.3\n", " },\n", " {\n", " 'factorId': '3',\n", " 'factorName': 'Momentum',\n", " 'factorCategory': 'Style',\n", " 'factorCategoryId': 'RI',\n", " 'factorReturn': 0.2\n", " }\n", " ],\n", " 'covarianceMatrix': [\n", " [0.089, 0.0123, 0.345],\n", " [0.0123, 0.767, 0.045],\n", " [0.345, 0.045, 0.0987]\n", " ],\n", " 'issuerSpecificCovariance': {\n", " 'universeId1': ['B02V2Q0', '6560713'],\n", " 'universeId2': ['B3Q15X5', '0709954'],\n", " 'covariance': [0.03754, 0.01234]\n", " },\n", " 'factorPortfolios': {\n", " 'universe': ['B02V2Q0', '6560713'],\n", " 'portfolio': [\n", " {\n", " 'factorId': '2',\n", " 'weights': [0.25, 0.75]\n", " },\n", " {\n", " 'factorId': '3',\n", " 'weights': [0.33, 0.66]\n", " },\n", " {\n", " 'factorId': '1',\n", " 'weights': [0.80, 0.20]\n", " }\n", " ]\n", " }\n", "}\n", "\n", "# use the flag mass-asset_batch_size to determine how many assets to upload at once if the data\n", "# payload is large enough to require splitting apart and uploading separately. A typical max_asset_batch_size\n", "# used by multiple vendors is 12000--if there are still frequent timeouts with this size try reducing the size\n", "model.upload_data(data, max_asset_batch_size=1000)\n", "\n", "# Check which days have data posted:\n", "dates_posted = model.get_dates()\n" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [ "### Upload Partial Data\n", "\n", "Users may also want to upload their data in separate stages. This is supported using the partial data upload function.\n", "Partial data must always include a date, and is combined with any data uploaded previously on that date. If repeat data is detected, the most recently uploaded data will replace the previously uploaded data.\n", "The target universe size describes the unique number of assets the API expects before it considers the data upload complete.\n", "\n", "For example, we can update the previously uploaded Issuer Specific Covariance data with a target universe size of 3, indicating\n", "that after this post, we should consider the Issuer Specific Covariance data complete on this date" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "from gs_quant.models.risk_model import RiskModelEventType\n", "\n", "partial_data = {\n", " 'date': '2021-01-13',\n", " 'issuerSpecificCovariance': {\n", " 'universeId1': ['BYVY8G0', '2073390'],\n", " 'universeId2': ['BYY88Y7', 'BYVY8G0'],\n", " 'covariance': [0.3754, 0.1234]\n", " }\n", "}\n", "\n", "model.upload_data(partial_data, max_asset_batch_size=10)\n", "\n", "# Check which days have issuer specific covariance data posted:\n", "isc_dates_posted = model.get_dates(event_type=RiskModelEventType.Risk_Model_ISC_Data)" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [ "## Make the Model Available on the Marquee UI\n", "\n", "The next step is enabling your model to be visible through the Marquee web interface by updating the risk model's coverage dataset with the risk model asset universe.\n" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "# The risk model will now appear in the dropdown on the \"create portfolios\" page once coverage is posted\n", "\n", "model.upload_asset_coverage_data()" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [ "## Enhance Factor Descriptions and Tool Tips\n", "\n", "The last optional step is adding tooltips and descriptions to the risk model factors. We highly encourage you to do this for every non-binary factor in your model (such as your style factors), so that Marquee UI users of your model can leverage the tooltips and descriptions to better understand how the factors were constructed and what they represent. " ], "metadata": { "collapsed": false } }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "from gs_quant.models.risk_model import FactorType, RiskModelFactor\n", "\n", "identifier = '3'\n", "tooltip = 'Short description that appears when you hover over the factor name on our UI.'\n", "description = 'Longer description that appears on the portfolio drill-down page of this factor.'\n", "glossary_description = 'Longest description to describe the factor in depth on our risk model glossary page.'\n", "\n", "factor = RiskModelFactor(\n", " identifier=identifier,\n", " type_=FactorType.Factor,\n", " tooltip=tooltip,\n", " description=description,\n", " glossary_description=glossary_description\n", ")\n", "\n", "model.save_factor_metadata(factor)" ], "metadata": { "collapsed": false, "pycharm": { "name": "#%%\n" } } }, { "cell_type": "markdown", "source": [], "metadata": { "collapsed": false, "pycharm": { "name": "#%% md\n" } } } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.6" } }, "nbformat": 4, "nbformat_minor": 0 }