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 save(self): """ Create a report using GsReportApi if it doesn't exist. Update the report if it does. """ target_report = TargetReport( position_source_id=self.position_source_id, position_source_type=self.position_source_type, type_=self.type, parameters=self.parameters) if self.id: target_report.id = self.id GsReportApi.update_report(target_report) else: GsReportApi.create_report(target_report)
def save(self): """ Create a report in Marquee if it doesn't exist. Update the report if it does. """ target_report = TargetReport(name=self.name, position_source_id=self.position_source_id, position_source_type=self.position_source_type, type_=self.type, parameters=self.parameters if self.parameters else ReportParameters()) if self.id: target_report.id = self.id GsReportApi.update_report(target_report) else: report = GsReportApi.create_report(target_report) self.__id = report.id
def get_factor_risk_report(self, risk_model_id: str = None, fx_hedged: bool = None) -> FactorRiskReport: if self.positioned_entity_type in [ EntityType.PORTFOLIO, EntityType.ASSET ]: position_source_type = self.positioned_entity_type.value.capitalize( ) reports = GsReportApi.get_reports( limit=100, position_source_type=position_source_type, position_source_id=self.id, report_type=f'{position_source_type} Factor Risk') if fx_hedged: reports = [ report for report in reports if report.parameters.fx_hedged == fx_hedged ] if risk_model_id: reports = [ report for report in reports if report.parameters.risk_model == risk_model_id ] if len(reports) > 1: raise MqError( f'This {position_source_type} has more than one factor risk report that matches ' 'your parameters. Please specify the risk model ID and fxHedged value in the ' 'function parameters.') if len(reports) == 0: raise MqError( f'This {position_source_type} has no factor risk reports that match your parameters.' ) return FactorRiskReport.from_target(reports[0]) raise NotImplementedError
def poll_report(self, report_id: str, timeout: int = 600, step: int = 30): poll = True end = dt.datetime.now() + dt.timedelta(seconds=timeout) while poll and dt.datetime.now() <= end: try: status = GsReportApi.get_report(report_id).status if status not in set(ReportStatus.error, ReportStatus.cancelled, ReportStatus.done): _logger.info( f'Report is {status} as of {dt.datetime.now().isoformat()}' ) time.sleep(step) else: poll = False if status == ReportStatus.error: raise MqError( f'Report {report_id} has failed for {self.id}. \ Please reach out to the Marquee team for assistance.' ) elif status == ReportStatus.cancelled: _logger.info( f'Report {report_id} has been cancelled. Please reach out to the \ Marquee team if you believe this is a mistake.' ) else: _logger.info(f'Report {report_id} is now complete') except Exception as err: raise MqError( f'Could not fetch report status with error {err}') raise MqError('The report is taking longer than expected to complete. \ Please check again later or reach out to the Marquee team for assistance.' )
def test_get_reports(mocker): id_1 = 'RX1' id_2 = 'RX2' mock_response = {'results': ( Report.from_dict({'id': id_1, 'positionSourceType': 'Portfolio', 'positionSourceId': 'MP1', 'type': 'Portfolio Performance Analytics', 'parameters': {'transactionCostModel': 'FIXED'}}), Report.from_dict({'id': id_2, 'positionSourceType': 'Portfolio', 'positionSourceId': 'MP2', 'type': 'Portfolio Performance Analytics', 'parameters': {'transactionCostModel': 'FIXED'}}) ), 'totalResults': 2} expected_response = ( Report(id=id_1, positionSourceType='Portfolio', positionSourceId='MP1', type='Portfolio Performance Analytics', parameters=ReportParameters(transactionCostModel='FIXED')), Report(id=id_2, positionSourceType='Portfolio', positionSourceId='MP2', type='Portfolio Performance Analytics', parameters=ReportParameters(transactionCostModel='FIXED')) ) # 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 = GsReportApi.get_reports() GsSession.current._get.assert_called_with('/reports?limit=100', cls=Report) assert response == expected_response
def test_get_report_jobs(mocker): id_1 = 'RJW55950MF0S0HKN' mock_response = {'results': ( ReportJob.from_dict({'id': id_1, 'positionSourceType': 'Portfolio', 'positionSourceId': 'MP1', 'parameters': {'transactionCostModel': 'FIXED'}}), ReportJob.from_dict({'id': id_1, 'positionSourceType': 'Portfolio', 'positionSourceId': 'MP2', 'parameters': {'transactionCostModel': 'FIXED'}}) ), 'totalResults': 2} expected_response = ( ReportJob(id=id_1, positionSourceType='Portfolio', positionSourceId='MP1', parameters=ReportParameters(transactionCostModel='FIXED')), ReportJob(id=id_1, positionSourceType='Portfolio', positionSourceId='MP2', parameters=ReportParameters(transactionCostModel='FIXED')) ) # 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 = GsReportApi.get_report_jobs(id_1) GsSession.current._get.assert_called_with('/reports/{id}/jobs'.format(id=id_1)) assert response == expected_response
def test_get_risk_factor_data_results(mocker): mock_response = [{ "date": "2003-01-03", "factor": "Value", "factorCategory": "Style", "pnl": 0.002877605406002121, "exposure": 12.105457414400002, "sensitivity": 0.026861678358408886, "proportionOfRisk": 0.0038230454885067183 }, { "date": "2003-01-06", "factor": "Value", "factorCategory": "Style", "pnl": 0, "exposure": 12.1028697664, "sensitivity": 0.02668134999120776, "proportionOfRisk": 0.0036290846167489335 }] # 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 = GsReportApi.get_risk_factor_data_results('reportId') GsSession.current._get.assert_called_with( '/risk/factors/reports/reportId/results') assert response == mock_response
def get_performance_report(self) -> PerformanceReport: reports = GsReportApi.get_reports( limit=1, position_source_type='Portfolio', position_source_id=self.id, report_type='Portfolio Performance Analytics') return PerformanceReport.from_target(reports[0]) if reports else None
def test_schedule_report(mocker): id_1 = 'RX1' start_date = dt.date(2019, 2, 18) end_date = dt.date(2019, 2, 19) mock_response = "" # mock GsSession mocker.patch.object(GsSession.__class__, 'default_value', return_value=GsSession.get(Environment.QA, 'client_id', 'secret')) mocker.patch.object(GsSession.current, '_post', return_value=mock_response) # run test response = GsReportApi.schedule_report(id_1, start_date, end_date) report_schedule_request = ReportScheduleRequest(startDate=start_date, endDate=end_date) GsSession.current._post.assert_called_with( '/reports/{id}/schedule'.format(id=id_1), report_schedule_request, cls=ReportScheduleRequest) assert response == mock_response
def __get_latest_create_report(self) -> Report: """ Used to find baskets's most recent price/publish info """ report = GsReportApi.get_reports(limit=1, position_source_id=self.id, report_type='Basket Create', order_by='>latestExecutionTime') return get(report, '0')
def result(self): """ :return: a Pandas DataFrame containing the results of the report job """ status = self.status() if status == ReportStatus.cancelled: raise MqValueError( 'This report job in status "cancelled". Cannot retrieve results.' ) if status == ReportStatus.error: raise MqValueError( 'This report job is in status "error". Cannot retrieve results.' ) if status != ReportStatus.done: raise MqValueError( 'This report job is not done. Cannot retrieve results.') if self.__report_type in [ ReportType.Portfolio_Factor_Risk, ReportType.Asset_Factor_Risk ]: results = GsReportApi.get_factor_risk_report_results( risk_report_id=self.__report_id, start_date=self.__start_date, end_date=self.__end_date) return pd.DataFrame(results) if self.__report_type == ReportType.Portfolio_Performance_Analytics: query = DataQuery(where={'reportId': self.__report_id}, start_date=self.__start_date, end_date=self.__end_date) results = GsDataApi.query_data( query=query, dataset_id=ReportDataset.PPA_DATASET.value) return pd.DataFrame(results) return None
def get_results(self, mode: FactorRiskResultsMode = FactorRiskResultsMode.Portfolio, factors: List[str] = None, factor_categories: List[str] = None, start_date: dt.date = None, end_date: dt.date = None, currency: Currency = None, return_format: ReturnFormat = ReturnFormat.DATA_FRAME) -> Union[Dict, pd.DataFrame]: """ Get the raw results associated with the factor risk report :param mode: results mode; defaults to the portfolio level :param factors: optional list of factors; defaults to all of them :param factor_categories: optional list of factor categories; defaults to all of them :param start_date: start date :param end_date: end date :param currency: currency :param return_format: return format; defaults to a Pandas DataFrame, but can be manually set to ReturnFormat.JSON :return: risk report results """ results = GsReportApi.get_factor_risk_report_results(risk_report_id=self.id, view=mode.value, factors=factors, factor_categories=factor_categories, currency=currency, start_date=start_date, end_date=end_date) return pd.DataFrame(results) if return_format == ReturnFormat.DATA_FRAME else results
def get_table(self, mode: FactorRiskTableMode = None, factors: List[str] = None, factor_categories: List[str] = None, date: dt.date = None, currency: Currency = None, order_by_column: str = None, order_type: OrderType = None, return_format: ReturnFormat = ReturnFormat.DATA_FRAME) -> Union[Dict, pd.DataFrame]: table = GsReportApi.get_factor_risk_report_table(risk_report_id=self.id, mode=mode, factors=factors, factor_categories=factor_categories, currency=currency, date=date, order_by_column=order_by_column, order_type=order_type) if return_format == ReturnFormat.DATA_FRAME: column_info = table.get('table').get('metadata').get('columnInfo') column_info[0].update({'columns': ['name', 'symbol', 'sector']}) rows = table.get('table').get('rows') sorted_columns = [] for column_group in column_info: sorted_columns = sorted_columns + column_group.get('columns') rows_data_frame = pd.DataFrame(rows) rows_data_frame = rows_data_frame[sorted_columns] rows_data_frame = rows_data_frame.set_index('name') return rows_data_frame return table
def update(self) -> Dict: """ Update your custom basket :return: dictionary containing asset id and report id **Usage** Make updates to your basket's metadata, pricing options, publishing options, or composition **See also** :func:`get_details` :func:`poll_status` :func:`create` """ self.__finish_populating_attributes_for_existing_basket() edit_inputs, rebal_inputs = self.__get_updates() if edit_inputs is None and rebal_inputs is None: raise MqValueError('Update failed: Nothing on the basket was changed') elif edit_inputs is not None and rebal_inputs is None: response = GsIndexApi.edit(self.id, edit_inputs) elif rebal_inputs is not None and edit_inputs is None: response = GsIndexApi.rebalance(self.id, rebal_inputs) else: response = self.__edit_and_rebalance(edit_inputs, rebal_inputs) gs_asset = GsAssetApi.get_asset(self.id) self.__latest_create_report = GsReportApi.get_report(response.report_id) self.__error_messages.remove(ErrorMessage.UNMODIFIABLE) self.__init__(gs_asset=gs_asset) return response.as_dict()
def get(cls, report_id: str, acceptable_types: List[ReportType] = None): # This map cant be instantiated / stored at the top of this file, bc the Factor/RiskReport classes aren't # defined there. Don't know the best place to put this report_type_to_class_type = { ReportType.Portfolio_Factor_Risk: type(FactorRiskReport()), ReportType.Asset_Factor_Risk: type(FactorRiskReport()), ReportType.Portfolio_Performance_Analytics: type(PerformanceReport()) } report = GsReportApi.get_report(report_id=report_id) if acceptable_types is not None and report.type not in acceptable_types: raise MqValueError('Unexpected report type found.') if report.type in report_type_to_class_type: return report_type_to_class_type[report.type]( report_id=report.id, position_source_id=report.position_source_id, position_source_type=report.position_source_type, report_type=report.type, parameters=report.parameters, status=report.status) return Report(report_id=report.id, position_source_id=report.position_source_id, position_source_type=report.position_source_type, report_type=report.type, parameters=report.parameters, status=report.status)
def test_get_report_status(mocker): id_1 = 'RJW55950MF0S0HKN' mock_response = ({ 'reportJobId': id_1, 'startDate': dt.date(2018, 12, 12), 'endDate': dt.date(2018, 12, 18) }, { 'reportJobId': id_1, 'startDate': dt.date(2017, 12, 12), 'endDate': dt.date(2017, 12, 16) }) # 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 = GsReportApi.get_report_status(id_1) GsSession.current._get.assert_called_with( '/reports/{id}/status'.format(id=id_1)) assert response == mock_response
def create(self) -> Dict: """ Create a new custom basket in Marquee :return: dictionary containing asset id and report id **Usage** Create a new custom basket in Marquee **See also** :func:`get_details` :func:`poll_status` :func:`update` """ inputs, pricing, publish = {}, {}, {} for prop in CustomBasketsCreateInputs.properties(): set_(inputs, prop, get(self, prop)) for prop in CustomBasketsPricingParameters.properties(): set_(pricing, prop, get(self, prop)) for prop in PublishParameters.properties(): set_(publish, prop, get(self, prop)) set_(inputs, 'position_set', self.position_set.to_target(common=False)) set_(inputs, 'pricing_parameters', CustomBasketsPricingParameters(**pricing)) set_(inputs, 'publish_parameters', PublishParameters(**publish)) create_inputs = CustomBasketsCreateInputs(**inputs) response = GsIndexApi.create(create_inputs) gs_asset = GsAssetApi.get_asset(response.asset_id) self.__latest_create_report = GsReportApi.get_report( response.report_id) self.__init__(gs_asset=gs_asset, _finish_init=True) return response.as_dict()
def test_update_report(mocker): id_1 = 'RX1' report = Report.from_dict({ 'id': id_1, 'positionSourceType': 'Portfolio', 'positionSourceId': 'MP25', 'type': 'Portfolio Performance Analytics', 'parameters': { 'transactionCostModel': 'FIXED' } }) # mock GsSession mocker.patch.object(GsSession.__class__, 'default_value', return_value=GsSession.get(Environment.QA, 'client_id', 'secret')) mocker.patch.object(GsSession.current, '_put', return_value=report) # run test response = GsReportApi.update_report(report) GsSession.current._put.assert_called_with('/reports/{id}'.format(id=id_1), report, cls=Report) assert response == report
def get(cls, report_id: str, **kwargs): """ Get a thematic report from the unique report identifier :param report_id: Marquee report ID :return: returns a ThematicReport object that correlates to the Marquee report """ return cls.from_target(GsReportApi.get_report(report_id))
def get_view(self, mode: FactorRiskViewsMode, factor: str = None, factor_category: str = None, start_date: dt.date = None, end_date: dt.date = None, currency: Currency = None) -> Dict: """ Get the results associated with the factor risk report as seen on the Marquee user interface :param mode: views mode :param factor: optional factor name :param factor_category: optional factor category :param start_date: start date :param end_date: end date :param currency: currency :return: risk report results """ return GsReportApi.get_factor_risk_report_view( risk_report_id=self.id, view=mode.value, factor=factor, factor_category=factor_category, currency=currency, start_date=start_date, end_date=end_date )
def test_get_factor_risk_report_view(mocker): report_id = 'RP123' mock_response = { "summary": { "riskModel": "BARRA_USSLOWL", "fxHedged": True, "assetCount": 1, "longExposure": 415, "shortExposure": 0, "factorProportionOfRisk": 70.28206437467601, "specificProportionOfRisk": 29.71793562532398, "date": "2021-08-12" } } # 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 = GsReportApi.get_factor_risk_report_view(report_id, view='Risk') GsSession.current._get.assert_called_with(f'/risk/factors/reports/{report_id}/views?view=Risk') assert response == mock_response
def get_performance_report(self) -> PerformanceReport: reports = GsReportApi.get_reports( limit=100, position_source_type='Portfolio', position_source_id=self.id, report_type='Portfolio Performance Analytics') if len(reports) == 0: raise MqError('This portfolio has no performance report.') return PerformanceReport.from_target(reports[0])
def get_most_recent_job(self): jobs = GsReportApi.get_report_jobs(self.id) most_current_job = sorted(jobs, key=lambda i: i['createdTime'], reverse=True)[0] return ReportJobFuture(report_id=self.id, job_id=most_current_job.get('id'), report_type=ReportType(most_current_job.get('reportType')), start_date=dt.datetime.strptime(most_current_job.get('startDate'), "%Y-%m-%d").date(), end_date=dt.datetime.strptime(most_current_job.get('endDate'), "%Y-%m-%d").date())
def get_factor_data(self, factor: str, start_date: dt.date = None, end_date: dt.date = None): return GsReportApi.get_risk_factor_data_results( risk_report_id=self.get_id(), factors=[factor], factor_categories=None, start_date=start_date, end_date=end_date)
def get_thematic_report(self) -> ThematicReport: if self.positioned_entity_type in [EntityType.PORTFOLIO, EntityType.ASSET]: position_source_type = self.positioned_entity_type.value.capitalize() reports = GsReportApi.get_reports(limit=100, position_source_type=position_source_type, position_source_id=self.id, report_type=f'{position_source_type} Thematic Analytics') if len(reports) == 0: raise MqError(f'This {position_source_type} has no thematic analytics report.') return ThematicReport.from_target(reports[0]) raise NotImplementedError
def get_factor_risk_reports(self, fx_hedged: bool = None) -> List[FactorRiskReport]: if self.positioned_entity_type in [EntityType.PORTFOLIO, EntityType.ASSET]: position_source_type = self.positioned_entity_type.value.capitalize() reports = GsReportApi.get_reports(limit=100, position_source_type=position_source_type, position_source_id=self.id, report_type=f'{position_source_type} Factor Risk') if fx_hedged: reports = [report for report in reports if report.parameters.fx_hedged == fx_hedged] if len(reports) == 0: raise MqError(f'This {position_source_type} has no factor risk reports that match your parameters.') return [FactorRiskReport.from_target(report) for report in reports] raise NotImplementedError
def get_results(self, factors: List[str] = None, factor_categories: List[str] = None, start_date: dt.date = None, end_date: dt.date = None, currency: Currency = None, return_format: ReturnFormat = ReturnFormat.DATA_FRAME) -> Union[Dict, pd.DataFrame]: results = GsReportApi.get_risk_factor_data_results(risk_report_id=self.id, factors=factors, factor_categories=factor_categories, currency=currency, start_date=start_date, end_date=end_date) return pd.DataFrame(results) if return_format == ReturnFormat.DATA_FRAME else results
def schedule(self, start_date: dt.date = None, end_date: dt.date = 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 = min(position_dates) if end_date is None: end_date = max(position_dates) GsReportApi.schedule_report(report_id=self.id, start_date=start_date, end_date=end_date)
def __edit_and_rebalance(self, edit_inputs: CustomBasketsEditInputs, rebal_inputs: CustomBasketsRebalanceInputs) -> CustomBasketsResponse: """ If updates require edit and rebalance, rebal will not be scheduled until/if edit report succeeds """ _logger.info('Current update request requires multiple reports. Your rebalance request will be submitted \ once the edit report has completed. Submitting basket edits now...') response = GsIndexApi.edit(self.id, edit_inputs) report_id = response.report_id self.__latest_create_report = GsReportApi.get_report(response.report_id) report_status = self.poll_report(report_id, timeout=600, step=15) if report_status != ReportStatus.done: raise MqError(f'The basket edit report\'s status is {status}. The current rebalance request will \ not be submitted in the meantime.') _logger.info('Your basket edits have completed successfuly. Submitting rebalance request now...') response = GsIndexApi.rebalance(self.id, rebal_inputs) return response