If you already have a portfolio in Marquee, the GS Quant SDK provides a simple and intuitive workflow to update positions and rerun reports.
To execute all the code in this tutorial, you will need the following application scopes:
If you are not yet permissioned for these scopes, please request them on your My Applications Page. If you have any other questions please reach out to the Marquee sales team.
You will also need to be an admin on the portfolio you would like to update. If you are not an admin, please ask a portfolio admin to edit the portfolio's entitlements to include you.
First you will import the necessary modules and add your client id and client secret.
import itertools
from time import sleep
from gs_quant.api.gs.assets import GsAssetApi
from gs_quant.api.gs.portfolios import GsPortfolioApi
from gs_quant.common import PositionSet
from gs_quant.markets.portfolio_manager import PortfolioManager
from gs_quant.session import GsSession, Environment
from gs_quant.target.portfolios import Position
client = 'ENTER CLIENT ID'
secret = 'ENTER CLIENT SECRET'
GsSession.use(Environment.PROD, client_id=client, client_secret=secret, scopes=('read_product_data read_financial_data modify_financial_data run_analytics',))
print('GS Session initialized.')
Portfolio positions in Marquee are stored on a holding basis, when means you only upload positions for days where you are rebalancing your portfolio. Take the following set of positions:
portfolio_id = 'ENTER PORTFOLIO ID'
positions = [
{ 'identifier': 'GS UN', 'quantity': 50, 'positionDate': '2020-05-01'},
{ 'identifier': 'AAPL UW', 'quantity': 25, 'positionDate': '2020-05-01'},
{ 'identifier': 'GS UN', 'quantity': 51, 'positionDate': '2020-07-01'},
{ 'identifier': 'AAPL UW', 'quantity': 26, 'positionDate': '2020-07-01'}
]
If these positions were to be uploaded correctly, that portfolio would hold 50 shares of GS UN and 25 shares of AAPL UW from May 1, 2020 to June 30, 2020, and it would hold 51 shares of GS UN and 26 shares of AAPL UW from July 1, 2020 to today (assuming the portfolio was initially empty).
Now let's proceed with updating these positions to our portfolio. The first step is to resolve the identifiers provided into their corresponding unique Marquee identifiers. In this case, positions are identified by Bloomberg ID, but other identifiers can be used and resolved by adding them to the fields
parameter in the function GsAssetApi.resolve_assets
.
all_identifiers = list(set([p['identifier'] for p in positions]))
results = GsAssetApi.resolve_assets(identifier=all_identifiers, fields=['bbid', 'id'], limit=1)
try:
identifier_to_marquee_id = dict(zip(results.keys(), [a[0]['id'] for a in results.values()]))
except:
unmapped_assets = {k for k,v in results.items() if not v}
raise ValueError('Error in resolving the following identifiers: ' + ', '.join(unmapped_assets))
print('Position identifiers successfully mapped as the following:')
for mq_id in identifier_to_marquee_id:
print(f'{mq_id} --> {identifier_to_marquee_id[mq_id]}')
Next we need to rearrange the data in the positions to fit the format expected in the Marquee API.
portfolio_position_sets = []
for position_date, positions_on_date in itertools.groupby(positions, lambda x: x['positionDate']):
formatted_positions = tuple(Position(asset_id=identifier_to_marquee_id[p['identifier']],
quantity=p['quantity']) for p in positions_on_date)
position_set = (PositionSet(position_date=position_date,
positions=formatted_positions))
portfolio_position_sets.append(position_set)
print('Portfolio positions successfully formatted.')
We're finally ready to update the portfolio with the positions.
GsPortfolioApi.update_positions(portfolio_id, portfolio_position_sets)
Once your portfolio has new positions, it's time to rerun all reports associated with the portfolio. You can do that easily using the PortfolioManager
class.
When running reports, you have the option of running them synchronously (meaning the function run_reports()
will wait for all reports to finish running to return their results), or asynchronously (meaning the function will return a list of ReportJobFuture
objects that will store the result of the report job once it is available). This tutorial will leverage the asyncronous feature.
pm = PortfolioManager(portfolio_id)
report_results = pm.run_reports(backcast=False, is_async=True)
for future in report_results:
while not future.done():
# you can use this time to perform other calculations or run other functions
sleep(5)
print(f'~~~~~~~~ Results ~~~~~~~~')
report_result = future.result()
print(report_result.__str__())
If you would only like to run reports on a specified range of dates, pass start_date
and end_date
datetime.date objects into the run_reports()
function parameters.
Other questions? Reach out to the Portfolio Analytics team!