def get(cls, portfolio_id: str = None, portfolio_name: str = None): if portfolio_name: portfolio = GsPortfolioApi.get_portfolio_by_name(portfolio_name) portfolio_id = portfolio.id position_date = PositionContext.current.position_date if PositionContext.is_entered else dt.date.today( ) portfolio = GsPortfolioApi.get_portfolio(portfolio_id) ret = Portfolio(name=portfolio.name) ret.__id = portfolio_id ret._get_instruments(position_date, True) return ret
def _schedule_reports(self, start_date, end_date, backcast: bool = False, show_progress: bool = True): GsPortfolioApi.schedule_reports(portfolio_id=self.id, start_date=start_date, end_date=end_date, backcast=backcast) if show_progress: self._show_report_progress()
def from_portfolio_id(portfolio_id: str, date=None): portfolio = GsPortfolioApi.get_portfolio(portfolio_id) response = GsPortfolioApi.get_positions_for_date(portfolio_id, date) if date else \ GsPortfolioApi.get_latest_positions(portfolio_id) response = response[0] if isinstance(response, tuple) else response positions = response.positions if isinstance( response, PositionSet) else response['positions'] instruments = GsAssetApi.get_instruments_for_positions(positions) ret = Portfolio(instruments, name=portfolio.name) ret.__id = portfolio_id return ret
def schedule_reports(self, start_date: dt.date = None, end_date: dt.date = None, backcast: bool = False): if None in [start_date, end_date]: suggested_schedule_dates = self.get_schedule_dates(backcast) start_date = start_date if start_date else suggested_schedule_dates[ 0] end_date = end_date if end_date else suggested_schedule_dates[1] GsPortfolioApi.schedule_reports(self.__portfolio_id, start_date, end_date, backcast=backcast)
def _get_instruments(self, position_date: dt.date, in_place: bool, return_priceables: bool = True): if self.id: dates_prior = list(filter(lambda date: date < position_date, GsPortfolioApi.get_position_dates(self.id))) if len(dates_prior) == 0: raise ValueError('Your portfolio has no positions on the PositionContext date') date = max(dates_prior) response = GsPortfolioApi.get_positions_for_date(self.id, date) positions = response.positions if response else [] instruments = GsAssetApi.get_instruments_for_positions(positions) if in_place: self.__priceables = instruments return instruments return self.__priceables if return_priceables else self.all_instruments
def test_get_many_portfolios(mocker): id_1 = 'MP1' id_2 = 'MP2' mock_response = {'results': ( Portfolio.from_dict({'id': id_1, 'currency': 'USD', 'name': 'Example Port 1'}), Portfolio.from_dict({'id': id_2, 'currency': 'USD', 'name': 'Example Port 2'}) ), 'totalResults': 2} expected_response = ( Portfolio(id=id_1, currency='USD', name='Example Port 1'), Portfolio(id=id_2, currency='USD', name='Example Port 2') ) # mock GsSession mocker.patch.object( GsSession.__class__, 'default_value', return_value=GsSession.get( Environment.QA, 'client_id', 'secret')) mocker.patch.object(GsSession.current, '_get', return_value=mock_response) # run test response = GsPortfolioApi.get_portfolios() GsSession.current._get.assert_called_with('/portfolios?&limit=100', cls=Portfolio) assert response == expected_response
def upload_custom_aum(self, aum_data: List[CustomAUMDataPoint], clear_existing_data: bool = None): """ Add AUM data for portfolio :param aum_data: list of AUM data to upload :param clear_existing_data: delete all previously uploaded AUM data for the portfolio (defaults to false) :return: """ formatted_aum_data = [{ 'date': data.date.strftime('%Y-%m-%d'), 'aum': data.aum } for data in aum_data] GsPortfolioApi.upload_custom_aum(self.portfolio_id, formatted_aum_data, clear_existing_data)
def pnl(portfolio_id: str, start_date: dt.date = None, end_date: dt.date = None, *, source: str = None, real_time: bool = False, request_id: Optional[str] = None) -> pd.Series: """ Returns the PnL of a portfolio :param portfolio_id: id of portfolio :param start_date: start date for getting pnl :param end_date: end date for getting pnl :param source: name of function caller :param real_time: whether to retrieve intraday data instead of EOD :param request_id: server request id :return: portfolio pnl values """ reports = GsPortfolioApi.get_reports(portfolio_id) performance_report_id = "" for report in reports: if report.type == ReportType.Portfolio_Performance_Analytics: performance_report_id = report.id data = PerformanceReport.get_pnl(performance_report_id, start_date, end_date) df = pd.DataFrame.from_records(data) df.set_index('date', inplace=True) return _extract_series_from_df(df, QueryType.PNL, True)
def test_get_latest_portfolio_positions(mocker): id_1 = 'MP1' date = dt.date(2019, 2, 18) mock_response = {'results': ( PositionSet.from_dict({ 'id': 'mock1', 'positionDate': '2019-02-18', 'lastUpdateTime': '2019-02-19T12:10:32.401Z', 'positions': [ {'assetId': 'MQA123', 'quantity': 0.3}, {'assetId': 'MQA456', 'quantity': 0.7} ] }), )} expected_response = ( PositionSet('mock1', date, dup.parse('2019-02-19T12:10:32.401Z'), ( Position(assetId='MQA123', quantity=0.3), Position(assetId='MQA456', quantity=0.7) )) ) # mock GsSession mocker.patch.object(GsSession.__class__, 'current', return_value=GsSession.get(Environment.QA, 'client_id', 'secret')) mocker.patch.object(GsSession.current, '_get', return_value=mock_response) # run test response = GsPortfolioApi.get_latest_positions(id_1) GsSession.current._get.assert_called_with('/portfolios/{id}/positions/last?type=close'.format(id=id_1), cls=PositionSet) assert response == expected_response
def get(cls, portfolio_id: str = None, name: str = None, **kwargs): if portfolio_id is None and name is None: raise MqValueError( 'Please specify a portfolio ID and/or portfolio name.') portfolios = GsPortfolioApi.get_portfolios( portfolio_ids=[portfolio_id] if portfolio_id else None, portfolio_names=[name] if name else None) if len(portfolios) == 0: raise ValueError( 'No portfolios in Marquee match the requested name and/or ID.') if len(portfolios) > 1: portfolios = { 'Name': [p.name for p in portfolios], 'ID': [p.id for p in portfolios], 'Created Time': [p.created_time for p in portfolios] } cls._print_dict(portfolios) raise ValueError( 'More than one portfolio matches the requested name and/or ID. To resolve,' ' please find the correct portfolio ID below and set it as your portfolio_id.' ) port = portfolios[0] return Portfolio(name=port.name, portfolio_id=port.id, currency=port.currency, entitlements=Entitlements.from_target( port.entitlements))
def from_portfolio_id(portfolio_id: str): response = GsPortfolioApi.get_latest_positions(portfolio_id) positions = response.positions if isinstance(response, PositionSet) else response['positions'] instruments = GsAssetApi.get_instruments_for_positions(positions) ret = Portfolio(instruments) ret.name = portfolio_id return ret
def get_aum_source(self) -> RiskAumSource: """ Get portfolio AUM Source :return: AUM Source """ portfolio = GsPortfolioApi.get_portfolio(self.portfolio_id) return portfolio.aum_source if portfolio.aum_source is not None else RiskAumSource.Long
def get_schedule_dates(self, backcast: bool = False) -> List[dt.date]: """ Get recommended start and end dates for a portfolio report scheduling job :param backcast: true if reports should be backcasted :return: a list of two dates, the first is the suggested start date and the second is the suggested end date """ return GsPortfolioApi.get_schedule_dates(self.id, backcast)
def schedule(self, start_date: dt.date = None, end_date: dt.date = None, backcast: bool = None): if None in [self.id, self.__position_source_id]: raise MqValueError( 'Can only schedule reports with valid IDs and Position Source IDs.' ) if self.position_source_type != PositionSourceType.Portfolio and None in [ start_date, end_date ]: raise MqValueError( 'Must specify schedule start and end dates for report.') if None in [start_date, end_date]: position_dates = GsPortfolioApi.get_position_dates( self.position_source_id) if len(position_dates) == 0: raise MqValueError( 'Cannot schedule reports for a portfolio with no positions.' ) if start_date is None: start_date = business_day_offset(min(position_dates) - relativedelta(years=1), -1, roll='forward') \ if backcast else min(position_dates) if end_date is None: end_date = min( position_dates) if backcast else business_day_offset( dt.date.today(), -1, roll='forward') GsReportApi.schedule_report(report_id=self.id, start_date=start_date, end_date=end_date, backcast=backcast)
def _create(self): # If a priceables portfolio, try resolving to MQ portfolio if self.__priceables: self.save() self.priceables = None return # If a positions portfolio, create using MQ API port = GsPortfolioApi.create_portfolio( portfolio=MQPortfolio(name=self.name, currency=self.currency, entitlements=self.entitlements.to_target())) PositionedEntity.__init__(self, port.id, EntityType.PORTFOLIO) Entity.__init__(self, port.id, EntityType.PORTFOLIO) self.__id = port.id self._PositionedEntity__entity_type = EntityType.PORTFOLIO self.entitlements = Entitlements.from_target(port.entitlements) self.currency = Currency(port.currency) # If the portfolio contains positions, upload them to the MQ portfolio and schedule reports if self.position_sets: position_sets = self.position_sets self.position_sets = None self.update_positions(position_sets, False) self._schedule_first_reports( [pos_set.date for pos_set in position_sets]) self.position_sets = None
def get_entitlements(self) -> Entitlements: if self.positioned_entity_type == EntityType.PORTFOLIO: response = GsPortfolioApi.get_portfolio(self.id) elif self.positioned_entity_type == EntityType.ASSET: response = GsAssetApi.get_asset(self.id) else: raise NotImplementedError return Entitlements.from_target(response.entitlements)
def save(self): if not self.name: raise ValueError('name not set') try: portfolio_id = GsPortfolioApi.get_portfolio_by_name(self.name).id except ValueError: from gs_quant.target.portfolios import Portfolio as MarqueePortfolio portfolio_id = GsPortfolioApi.create_portfolio(MarqueePortfolio('USD', self.name)).id _logger.info('Created Marquee portfolio with ID: {}'.format(portfolio_id)) position_set = PositionSet( position_date=dt.date.today(), positions=tuple(Position(asset_id=GsAssetApi.get_or_create_asset_from_instrument(i)) for i in self.instruments)) GsPortfolioApi.update_positions(portfolio_id, (position_set,))
def save(self, overwrite: Optional[bool] = False): if self.portfolios: raise ValueError('Cannot save portfolios with nested portfolios') if self.__id: if not overwrite: raise ValueError(f'Portfolio with id {id} already exists. Use overwrite=True to overwrite') else: if not self.name: raise ValueError('name not set') self.__id = GsPortfolioApi.create_portfolio(MarqueePortfolio('USD', self.name)).id _logger.info(f'Created Marquee portfolio {self.name} with id {self.__id}') position_set = PositionSet( position_date=self.__position_context.position_date, positions=tuple(Position(asset_id=GsAssetApi.get_or_create_asset_from_instrument(i)) for i in self.instruments)) if len(position_set.positions) > 0: GsPortfolioApi.update_positions(self.__id, [position_set])
def get_positions_data(self, start: dt.date = DateLimit.LOW_LIMIT.value, end: dt.date = dt.date.today(), fields: [str] = None, position_type: PositionType = PositionType.CLOSE) -> List[Dict]: if self.positioned_entity_type == EntityType.ASSET: return GsAssetApi.get_asset_positions_data(self.id, start, end, fields, position_type) if self.positioned_entity_type == EntityType.PORTFOLIO: return GsPortfolioApi.get_positions_data(self.id, start, end, fields, position_type) raise NotImplementedError
def get_latest_position_set(self, position_type: PositionType = PositionType.CLOSE) -> PositionSet: if self.positioned_entity_type == EntityType.ASSET: response = GsAssetApi.get_latest_positions(self.id, position_type) return PositionSet.from_target(response) if self.positioned_entity_type == EntityType.PORTFOLIO: response = GsPortfolioApi.get_latest_positions(portfolio_id=self.id, position_type=position_type.value) return PositionSet.from_target(response) raise NotImplementedError
def test_get_portfolio_positions(mocker): id_1 = 'MP1' start_date = dt.date(2019, 2, 18) end_date = dt.date(2019, 2, 19) mock_response = {'positionSets': ( { 'id': 'mock1', 'positionDate': '2019-02-18', 'lastUpdateTime': '2019-02-19T12:10:32.401Z', 'positions': [ {'assetId': 'MQA123', 'quantity': 0.3}, {'assetId': 'MQA456', 'quantity': 0.7} ] }, { 'id': 'mock2', 'positionDate': '2019-02-19', 'lastUpdateTime': '2019-02-20T05:04:32.981Z', 'positions': [ {'assetId': 'MQA123', 'quantity': 0.4}, {'assetId': 'MQA456', 'quantity': 0.6} ] } )} expected_response = ( PositionSet('mock1', start_date, dup.parse('2019-02-19T12:10:32.401Z'), ( Position(assetId='MQA123', quantity=0.3), Position(assetId='MQA456', quantity=0.7) )), PositionSet('mock2', end_date, dup.parse('2019-02-20T05:04:32.981Z'), ( Position(assetId='MQA123', quantity=0.4), Position(assetId='MQA456', quantity=0.6) )) ) # mock GsSession mocker.patch.object( GsSession.__class__, 'current', return_value=GsSession.get( Environment.QA, 'client_id', 'secret')) mocker.patch.object(GsSession.current, '_get', return_value=mock_response) # run test response = GsPortfolioApi.get_positions(id_1, start_date, end_date) GsSession.current._get.assert_called_with( '/portfolios/{id}/positions?type=close&startDate={sd}&endDate={ed}'.format(id=id_1, sd=start_date, ed=end_date)) assert response == expected_response
def update_positions(self, position_sets: List[PositionSet], net_positions: bool = True): if self.positioned_entity_type == EntityType.PORTFOLIO: if not position_sets: return currency = GsPortfolioApi.get_portfolio(self.id).currency new_sets = [] for pos_set in position_sets: if pos_set.reference_notional is None: incorrect_set = any([pos.quantity is None or pos.weight is not None for pos in pos_set.positions]) if incorrect_set: raise MqValueError('If you would like to upload position sets without notionals, ' 'every position must have a quantity and cannot have a weight.') new_sets.append(pos_set) else: new_sets.append(self._convert_pos_set_with_weights(pos_set, currency)) GsPortfolioApi.update_positions(portfolio_id=self.id, position_sets=[p.to_target() for p in new_sets]) time.sleep(3) else: raise NotImplementedError
def update_positions(self, position_sets: List[PositionSet], schedule_reports: bool = True): if self.positioned_entity_type == EntityType.PORTFOLIO: if not position_sets: return existing_positions_dates = self.get_position_dates() new_position_dates = [p.date for p in position_sets] if position_sets else [] reports = [r.latest_end_date for r in self.get_reports() if r.latest_end_date] latest_date_covered_by_reports = min(reports) if reports else None latest_position_date_in_reports = max([d for d in existing_positions_dates if d <= latest_date_covered_by_reports]) \ if latest_date_covered_by_reports else min(new_position_dates) start_date = min(latest_position_date_in_reports, min(new_position_dates)) end_date = max(new_position_dates) GsPortfolioApi.update_positions(portfolio_id=self.id, position_sets=[p.to_target() for p in position_sets]) if schedule_reports: self._schedule_reports(start_date=start_date, end_date=end_date) else: raise NotImplementedError
def get_position_set_for_date(self, date: dt.date, position_type: PositionType = PositionType.CLOSE) -> PositionSet: if self.positioned_entity_type == EntityType.ASSET: response = GsAssetApi.get_asset_positions_for_date(self.id, date, position_type)[0] return PositionSet.from_target(response) if self.positioned_entity_type == EntityType.PORTFOLIO: response = GsPortfolioApi.get_positions_for_date(portfolio_id=self.id, position_date=date, position_type=position_type.value) return PositionSet.from_target(response) if response else None raise NotImplementedError
def save_as_quote(self, overwrite: Optional[bool] = False) -> str: if self.portfolios: raise ValueError('Cannot save portfolios with nested portfolios') request = RiskRequest( tuple(RiskPosition(instrument=i, quantity=i.instrument_quantity) for i in self.instruments), (ResolvedInstrumentValues,), pricing_and_market_data_as_of=(PricingDateAndMarketDataAsOf(pricing_date=self._pricing_context.pricing_date, market=self._pricing_context.market),) ) if self.__quote_id: if not overwrite: raise ValueError(f'Quote with id {self.__quote_id} already exists. Use overwrite=True to overwrite') else: GsPortfolioApi.update_quote(self.__quote_id, request) _logger.info(f'Updated quote with id {self.__quote_id}') else: self.__quote_id = GsPortfolioApi.save_quote(request) _logger.info(f'Created quote with id {self.__quote_id}') return self.__quote_id
def save_to_shadowbook(self, name: str): if self.portfolios: raise ValueError('Cannot save portfolios with nested portfolios') request = RiskRequest( tuple(RiskPosition(instrument=i, quantity=i.instrument_quantity) for i in self.instruments), (ResolvedInstrumentValues,), pricing_and_market_data_as_of=(PricingDateAndMarketDataAsOf(pricing_date=self._pricing_context.pricing_date, market=self._pricing_context.market),) ) status = GsPortfolioApi.save_to_shadowbook(request, name) print(f'Save to shadowbook status - {status}')
def test_get_portfolio(mocker): id_1 = 'MP1' mock_response = Portfolio(id=id_1, currency='USD', name='Example Port') # mock GsSession mocker.patch.object(GsSession.__class__, 'current', return_value=GsSession.get(Environment.QA, 'client_id', 'secret')) mocker.patch.object(GsSession.current, '_get', return_value=mock_response) # run test response = GsPortfolioApi.get_portfolio(id_1) GsSession.current._get.assert_called_with('/portfolios/{id}'.format(id=id_1), cls=Portfolio) assert response == mock_response
def get_position_sets(self, start: dt.date = DateLimit.LOW_LIMIT.value, end: dt.date = dt.date.today(), position_type: PositionType = PositionType.CLOSE) -> List[PositionSet]: if self.positioned_entity_type == EntityType.ASSET: response = GsAssetApi.get_asset_positions_for_dates(self.id, start, end, position_type) return [PositionSet.from_target(position_set) for position_set in response] if self.positioned_entity_type == EntityType.PORTFOLIO: response = GsPortfolioApi.get_positions(portfolio_id=self.id, start_date=start, end_date=end) return [PositionSet.from_target(position_set) for position_set in response] raise NotImplementedError
def test_delete_portfolio(mocker): id_1 = 'MP1' mock_response = "Successfully deleted portfolio." # mock GsSession mocker.patch.object(GsSession.__class__, 'current', return_value=GsSession.get(Environment.QA, 'client_id', 'secret')) mocker.patch.object(GsSession.current, '_delete', return_value=mock_response) # run test response = GsPortfolioApi.delete_portfolio(id_1) GsSession.current._delete.assert_called_with('/portfolios/{id}'.format(id=id_1)) assert response == mock_response
def test_create_portfolio(mocker): id_1 = 'MP1' portfolio = Portfolio(id=id_1, currency='USD', name='Example Port') # mock GsSession mocker.patch.object(GsSession.__class__, 'current', return_value=GsSession.get(Environment.QA, 'client_id', 'secret')) mocker.patch.object(GsSession.current, '_post', return_value=portfolio) # run test response = GsPortfolioApi.create_portfolio(portfolio) GsSession.current._post.assert_called_with('/portfolios', portfolio, cls=Portfolio) assert response == portfolio