#!/usr/bin/env python # coding: utf-8 # # Uploading Custom History when Creating a Custom Basket # # When creating a custom basket, Marquee by default will publish up to five years of pricing and composition history based # on the composition entered during creation. If you choose to bypass this behavior by setting the `default_backcast` # parameter to `False`, you may upload your own custom history any time after the basket has been created. This tutorial # will show you how to submit a basket backcast, including best practices to avoid mapping errors, and more! # # **Note:** You must specify this setting during creation in order to perform the below steps. If you'd like to submit # custom history for a basket that has already been created and did not originally select this option, you'll need to # create a new basket. # ## Step 1: Authenticate & Initialize your session # # First you will import the necessary modules and add your client id and client secret. # In[ ]: import datetime as dt from gs_quant.api.gs.reports import GsReportApi from gs_quant.markets.baskets import Basket from gs_quant.markets.position_set import Currency, Position, PositionSet, PositionSetWeightingStrategy from gs_quant.session import Environment, GsSession client = 'CLIENT ID' secret = 'CLIENT SECRET' GsSession.use(Environment.PROD, client_id=client, client_secret=secret, scopes=('modify_product_data',)) # ## Step 2: Extract your positions into dated position sets # # Next you'll need to create a PositionSet for each date your basket had position changes historically. Examples of # position changes are anything that affect the price or composition of your basket. For example, periodic weighted # rebalances, identifier changes or other corporate actions, adds/drops, etc. # # When Marquee goes to upload your history, we'll convert your position weights into quantities and carry those forward # until the next date with position changes occurs. If you do not have position weights and only have quantities at the # time of upload, make sure to follow step #4. # # For the sake of simplicity in this tutorial, we'll show a very basic example of position sets that may represent # a basket's composition history. However based on your own personal Input/Output preferences and setup, you'll likely want to make # some adjustments in order to aggregate your positions into unique PositionSet objects per date. See # [position_set examples](../examples/03_basket_creation/position_set/0012_create_historical_position_sets_from_excel.ipynb) # for alternate methods. # In[ ]: positions = [Position(identifier='AAPL UW', weight=0.5), Position(identifier='MSFT UW', weight=0.5)] pos_set_1 = PositionSet(positions, dt.date(2021, 6, 3)) pos_set_2 = PositionSet(positions, dt.date(2022, 1, 2)) pos_set_3 = PositionSet(positions, dt.date(2022, 6, 1)) pos_set_4 = PositionSet(positions, dt.date(2023, 1, 4)) position_sets = [pos_set_1, pos_set_2, pos_set_3, pos_set_4] # ## Step 3: Resolve your position sets and check for unmapped assets # # Now that you have your position sets created, it's time to resolve your positions historically. This will help confirm # that each asset identifier can be mapped as of the provided position date. # # If you face any mapping issues, double check that your identifiers are valid. Consider identifier changes (e.g., # FB UW -> META UW), listed status, IPOs, etc. If you believe you have corrected all assets with these issues and are still # experiencing problems mapping your positions, you may remove these identifiers or email the # [baskets support team](mailto:gs-marquee-baskets-support@gs.com?subject=Failed%20To%20Map%20Positions%20for%20Basket%20Backcast) # for assistance. # In[ ]: for position_set in position_sets: position_set.resolve() if position_set.unresolved_positions is not None and len(position_set.unresolved_positions): print(f'Error resolving assets on {position_set.date.strftime("%Y-%m-%d")}: {position_set.unresolved_positions}') """ Uncomment the below to removed unresolved positions from your position set """ # position_set.remove_unresolved_positions() # ## Step 4 (Optional): Convert position quantities to weights # # Basket backcasts require each position to have a specified weight. If you only have historical quantities, you may # call the `price` function on each position set in order to extract these weights via Marquee. Otherwise you may skip # this step. # # Similar to step #3, you can double check if any assets are unable to be priced and either remove these from your # position set or contact the [baskets support team](mailto:gs-marquee-baskets-support@gs.com?subject=Failed%20To%20Price%20Positions%20for%20Basket%20Backcast) # for help. # In[ ]: currency = Currency.USD # replace with desired currency for position_set in position_sets: position_set.price(currency=currency, use_unadjusted_close_price=False, weighting_strategy=PositionSetWeightingStrategy.Quantity) if position_set.unpriced_positions is not None and len(position_set.unpriced_positions): print(f'Error pricing assets on {position_set.date.strftime("%Y-%m-%d")}: {position_set.unpriced_positions}') """ Uncomment the below to removed unpriced positions from your position set """ # position_set.remove_unpriced_positions() # ### Quick Tip! # If you had to remove positions from any position set for one of the reasons described above, the total weight of # each position might no longer add up to 1. If this is the case, you can call `redistribute_weights` on any misweighted # position sets, which will redistribute the remaining weights proportionally among each position. # In[ ]: for position_set in position_sets: position_set.redistribute_weights() # ## Step 5: Fetch your basket and upload your history # # Once your positions have all been mapped and assigned weights, you're ready to submit to Marquee! Fetch your basket # and call `upload_position_history` using the position sets from above. # In[ ]: basket = Basket.get('GSMBXXXX') basket.upload_position_history(position_sets) # ## Step 6 (Optional): Reschedule or Cancel failed report jobs # # There are several cases that can cause a backcast report to fail, including long history length, large number of # positions/rebalance frequency, historical errors for assets in Marquee, and more. The above script will catch as # many issues as possible prior to upload but there are sometimes still failures that require investigation from the # support team to fix. # # If you find that your backcasts are failing, you can reschedule any failed report jobs using the below snippet to catch # for intermittent errors such as timeouts or memory issues on Marquee side. If the errors persist, you may reach out # to the [baskets support team](mailto:gs-marquee-baskets-support@gs.com) for further information. # # If your backcast reports are failing due a non-Marquee issue and you need to re-upload your position history, you'll # need to cancel any failed report jobs in order to resubmit your adjusted positions. Then you may repeat steps #1-5. # In[ ]: backcast_reports = GsReportApi.get_reports(position_source_id=basket.get_marquee_id(), report_type='Basket Backcast') report_ids, report_job_ids = [r.id for r in backcast_reports], [] for report_id in report_ids: report_jobs = GsReportApi.get_report_jobs(report_id) report_job_ids = [rj.get('id') for rj in report_jobs if rj.get('status') == 'error'] """ Use this to RESCHEDULE failed report jobs """ # for report_job in report_job_ids: # print(f'Rescheduling report job {report_job.get("id")}') # GsReportApi.reschedule_report_job(report_job.get('id')) """ Use this to CANCEL failed report jobs """ # for report_job in report_job_ids: # print(f'Cancelling report job {report_job.get("id")}') # GsReportApi.cancel_report_job(report_job.get('id'))