示例#1
0
 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
示例#2
0
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_normalized_performance_short():
    idx = pd.date_range('2020-01-02', freq='D', periods=3)
    replace = Replacer()
    expected = {"Short": pd.Series(data=[1, 1 / 2, 1 / 3], index=idx,
                                   name='normalizedPerformance', dtype='float64'),
                "Long": pd.Series(data=[1, 2, 3], index=idx,
                                  name='normalizedPerformance', dtype='float64'),
                None: pd.Series(data=[1, (2 + 1 / 2) / 2, (3 + 1 / 3) / 2], index=idx,
                                name='normalizedPerformance', dtype='float64')}

    mock = replace('gs_quant.api.gs.portfolios.GsPortfolioApi.get_reports', Mock())
    mock.return_value = [
        Report.from_dict({'id': 'RP1', 'positionSourceType': 'Portfolio', 'positionSourceId': 'MP1',
                          'type': 'Portfolio Performance Analytics',
                          'parameters': {'transactionCostModel': 'FIXED'}})]
    # mock PerformanceReport.get_portfolio_constituents()
    mock = replace('gs_quant.markets.report.PerformanceReport.get_portfolio_constituents', Mock())
    mock.return_value = MarketDataResponseFrame(data=constituents_data_l_s, dtype="float64")

    # mock PerformanceReport.get()
    mock = replace('gs_quant.markets.report.PerformanceReport.get', Mock())
    mock.return_value = PerformanceReport(report_id='RP1',
                                          position_source_type='Portfolio',
                                          position_source_id='MP1',
                                          report_type='Portfolio Performance Analytics',
                                          parameters=ReportParameters(transaction_cost_model='FIXED'))

    for k, v in expected.items():
        with DataContext(datetime.date(2020, 1, 1), datetime.date(2019, 1, 3)):
            actual = mr.normalized_performance('MP1', k)
            assert all((actual.values - v.values) < 0.01)
    replace.restore()
示例#4
0
 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])
示例#5
0
def test_normalized_performance_default_aum():
    idx = pd.date_range('2020-01-02', freq='D', periods=3)
    expected = pd.Series(data=[1, 1 / 0.8, 1 / 0.6],
                         index=idx,
                         name='normalizedPerformance',
                         dtype='float64')

    with DataContext(datetime.date(2020, 1, 1), datetime.date(2019, 1, 3)):

        df = MarketDataResponseFrame(data=ppa_data, dtype="float64")
        replace = Replacer()

        # mock GsPortfolioApi.get_reports()
        mock = replace('gs_quant.api.gs.portfolios.GsPortfolioApi.get_reports',
                       Mock())
        mock.return_value = [
            Report.from_dict({
                'id': 'RP1',
                'positionSourceType': 'Portfolio',
                'positionSourceId': 'MP1',
                'type': 'Portfolio Performance Analytics',
                'parameters': {
                    'transactionCostModel': 'FIXED'
                }
            })
        ]

        # mock PerformanceReport.get_many_measures()
        mock = replace(
            'gs_quant.markets.report.PerformanceReport.get_many_measures',
            Mock())
        mock.return_value = df

        # mock PerformanceReport.get_custom_aum()
        mock = replace(
            'gs_quant.api.gs.portfolios.GsPortfolioApi.get_custom_aum', Mock())
        mock.return_value = aum

        # mock PerformanceReport.get()
        mock = replace('gs_quant.markets.report.PerformanceReport.get', Mock())
        mock.return_value = PerformanceReport(
            report_id='RP1',
            position_source_type='Portfolio',
            position_source_id='MP1',
            report_type='Portfolio Performance Analytics',
            parameters=ReportParameters(transaction_cost_model='FIXED'))

        mock = replace(
            'gs_quant.api.gs.portfolios.GsPortfolioApi.get_portfolio', Mock())
        mock.return_value = Portfolio('USD', 'P1', id_='MP1')

        actual = mr.normalized_performance('MP1', None)
        assert all(actual.values == expected.values)
        replace.restore()
示例#6
0
文件: entity.py 项目: shlff/gs-quant
 def get_reports(self) -> List[Report]:
     if self.positioned_entity_type == EntityType.PORTFOLIO:
         reports_as_target = GsPortfolioApi.get_reports(portfolio_id=self.id)
         report_objects = []
         for report in reports_as_target:
             if report.type == ReportType.Portfolio_Performance_Analytics:
                 report_objects.append(PerformanceReport.from_target(report))
             elif report.type in [ReportType.Portfolio_Factor_Risk, ReportType.Asset_Factor_Risk]:
                 report_objects.append(FactorRiskReport.from_target(report))
             else:
                 report_objects.append(Report.from_target(report))
         return report_objects
     raise NotImplementedError
示例#7
0
def test_normalized_performance_no_custom_aum():
    with DataContext(datetime.date(2020, 1, 1), datetime.date(2019, 1, 3)):
        df = MarketDataResponseFrame(data=ppa_data, dtype="float64")
        replace = Replacer()

        # mock GsPortfolioApi.get_reports()
        mock = replace('gs_quant.api.gs.portfolios.GsPortfolioApi.get_reports',
                       Mock())
        mock.return_value = [
            Report.from_dict({
                'id': 'RP1',
                'positionSourceType': 'Portfolio',
                'positionSourceId': 'MP1',
                'type': 'Portfolio Performance Analytics',
                'parameters': {
                    'transactionCostModel': 'FIXED'
                }
            })
        ]

        # mock PerformanceReport.get_many_measures()
        mock = replace(
            'gs_quant.markets.report.PerformanceReport.get_many_measures',
            Mock())
        mock.return_value = df

        # mock PerformanceReport.get_portfolio_constituents()
        mock = replace(
            'gs_quant.markets.report.PerformanceReport.get_portfolio_constituents',
            Mock())
        mock.return_value = MarketDataResponseFrame(data=constituents_data,
                                                    dtype="float64")

        # mock PerformanceReport.get_custom_aum()
        mock = replace(
            'gs_quant.api.gs.portfolios.GsPortfolioApi.get_custom_aum', Mock())
        mock.return_value = pd.DataFrame({})

        # mock PerformanceReport.get()
        mock = replace('gs_quant.markets.report.PerformanceReport.get', Mock())
        mock.return_value = PerformanceReport(
            report_id='RP1',
            position_source_type='Portfolio',
            position_source_id='MP1',
            report_type='Portfolio Performance Analytics',
            parameters=ReportParameters(transaction_cost_model='FIXED'))

        with pytest.raises(MqError):
            mr.normalized_performance('MP1', 'Custom AUM')
        replace.restore()
示例#8
0
 def get_reports(self) -> List[Report]:
     """
     Get a list of all reports associated with the portfolio
     :return: list of Report objects
     """
     reports = []
     reports_as_targets = GsPortfolioApi.get_reports(self.__portfolio_id)
     for report_target in reports_as_targets:
         if report_target.type in [
                 ReportType.Portfolio_Factor_Risk,
                 ReportType.Asset_Factor_Risk
         ]:
             reports.append(FactorRiskReport.from_target(report_target))
         if report_target.type == ReportType.Portfolio_Performance_Analytics:
             reports.append(PerformanceReport.from_target(report_target))
     return reports
示例#9
0
def test_get_performance_report(mocker):
    # 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=Report(
                            id='PPAID',
                            position_source_type=PositionSourceType.Portfolio,
                            position_source_id='PORTFOLIOID',
                            parameters=None,
                            type=ReportType.Portfolio_Performance_Analytics,
                            status=ReportStatus.done))
    # run test
    response = PerformanceReport.get('PPAID')
    assert response.type == ReportType.Portfolio_Performance_Analytics
示例#10
0
def test_financial_conditions_index():
    data = {
        'pnl': [101, 102, 103],
        'date': ['2020-01-01', '2020-01-02', '2020-01-03']
    }
    idx = pd.date_range('2020-01-01', freq='D', periods=3)
    df = MarketDataResponseFrame(data=data, index=idx)
    df.dataset_ids = ('PNL', )
    replace = Replacer()

    # mock PerformanceReport.get_pnl()
    mock = replace('gs_quant.markets.report.PerformanceReport.get_pnl', Mock())
    mock.return_value = df

    # mock GsPortfolioApi.get_reports()
    mock = replace('gs_quant.api.gs.portfolios.GsPortfolioApi.get_reports',
                   Mock())
    mock.return_value = [
        Report.from_dict({
            'id': 'RP1',
            'positionSourceType': 'Portfolio',
            'positionSourceId': 'MP1',
            'type': 'Portfolio Performance Analytics',
            'parameters': {
                'transactionCostModel': 'FIXED'
            }
        })
    ]

    # mock PerformanceReport.get()
    mock = replace('gs_quant.markets.report.PerformanceReport.get', Mock())
    mock.return_value = PerformanceReport(
        report_id='RP1',
        position_source_type='Portfolio',
        position_source_id='MP1',
        report_type='Portfolio Performance Analytics',
        parameters=ReportParameters(transaction_cost_model='FIXED'))

    with DataContext(datetime.date(2020, 1, 1), datetime.date(2019, 1, 3)):
        actual = mp.portfolio_pnl('MP1')
        assert actual.index.equals(idx)
        assert all(actual.values == data['pnl'])
    replace.restore()
示例#11
0
def short_pnl(report_id: str, *, source: str = None,
              real_time: bool = False, request_id: Optional[str] = None) -> pd.Series:
    """

    PNL from short holdings
    :param report_id: id of performance report
    :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 short pnl
    """
    start_date = DataContext.current.start_time.date()
    end_date = DataContext.current.end_time.date()
    performance_report = PerformanceReport.get(report_id)

    constituent_data = performance_report.get_portfolio_constituents(
        fields=['pnl', 'quantity'], start_date=start_date, end_date=end_date).set_index('date')
    short_leg = constituent_data[constituent_data['quantity'] < 0]['pnl']
    short_leg = short_leg.groupby(level=0).sum()
    return pd.Series(short_leg, name="shortPnl")
def test_get_short_pnl():
    idx = pd.date_range('2020-01-02', freq='D', periods=3)
    replace = Replacer()
    expected = pd.Series(data=[0, -2, -2],
                         index=idx,
                         name='shortPnl',
                         dtype='float64')

    mock = replace('gs_quant.api.gs.portfolios.GsPortfolioApi.get_reports',
                   Mock())
    mock.return_value = [
        Report.from_dict({
            'id': 'RP1',
            'positionSourceType': 'Portfolio',
            'positionSourceId': 'MP1',
            'type': 'Portfolio Performance Analytics',
            'parameters': {
                'transactionCostModel': 'FIXED'
            }
        })
    ]
    # mock PerformanceReport.get_portfolio_constituents()
    mock = replace(
        'gs_quant.markets.report.PerformanceReport.get_portfolio_constituents',
        Mock())
    mock.return_value = MarketDataResponseFrame(data=pnl_data_l_s)

    # mock PerformanceReport.get()
    mock = replace('gs_quant.markets.report.PerformanceReport.get', Mock())
    mock.return_value = PerformanceReport(
        report_id='RP1',
        position_source_type='Portfolio',
        position_source_id='MP1',
        report_type='Portfolio Performance Analytics',
        parameters=ReportParameters(transaction_cost_model='FIXED'))

    with DataContext(datetime.date(2020, 1, 1), datetime.date(2019, 1, 3)):
        actual = mr.short_pnl('MP1')
        assert all(actual.values == expected.values)
    replace.restore()
def test_get_long_pnl_empty():
    replace = Replacer()
    expected = pd.Series(dtype=float)

    mock = replace('gs_quant.api.gs.portfolios.GsPortfolioApi.get_reports',
                   Mock())
    mock.return_value = [
        Report.from_dict({
            'id': 'RP1',
            'positionSourceType': 'Portfolio',
            'positionSourceId': 'MP1',
            'type': 'Portfolio Performance Analytics',
            'parameters': {
                'transactionCostModel': 'FIXED'
            }
        })
    ]
    # mock PerformanceReport.get_portfolio_constituents()
    mock = replace(
        'gs_quant.markets.report.PerformanceReport.get_portfolio_constituents',
        Mock())
    mock.return_value = MarketDataResponseFrame(data=constituents_data_s)

    # mock PerformanceReport.get()
    mock = replace('gs_quant.markets.report.PerformanceReport.get', Mock())
    mock.return_value = PerformanceReport(
        report_id='RP1',
        position_source_type='Portfolio',
        position_source_id='MP1',
        report_type='Portfolio Performance Analytics',
        parameters=ReportParameters(transaction_cost_model='FIXED'))

    with DataContext(datetime.date(2020, 1, 1), datetime.date(2019, 1, 3)):
        actual = mr.long_pnl('MP1')
        assert all(actual.values == expected.values)
    replace.restore()
示例#14
0
def normalized_performance(report_id: str,
                           aum_source: str = None,
                           *,
                           source: str = None,
                           real_time: bool = False,
                           request_id: Optional[str] = None) -> pd.Series:
    """
    Returns the Normalized Performance of a performance report based on AUM source
    :param report_id: id of performance report
    :param aum_source: source to normalize pnl from, default is the aum source on your portfolio,
                if no aum source is set on your portfolio the default is gross
    :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 normalized performance

    **Usage**

    Returns the normalized performance of the portfolio based on AUM source.

    If :math:`aum_source` is "Custom AUM":
    We read AUM from custom AUM uploaded to that portfolio and normalize performance based on that exposure

    If :math:`aum_source` is one of: Long, Short, RiskAumSource.Net,
        AumSource.Gross, we take these exposures from the calculated exposures based on daily positions

    :math:`NP(L/S)_{t} = SUM( PNL(L/S)_{t}/ ( EXP(L/S)_{t} ) - cPNL(L/S)_{t-1) )
        if ( EXP(L/S)_{t} ) - cPNL(L/S)_{t-1)) > 0
        else:
            1/ SUM( PNL(L/S)_{t}/ ( EXP(L/S)_{t} ) - cPNL(L/S)_{t-1) )`
    For each leg, short and long, then:
    :math:`NP_{t} = NP(L)_{t} * (EXP(L)_{t} / AUM_{t}) + NP(S)_{t} * (EXP(S)_{t} / AUM_{t}) + 1`

    This takes into account varying AUM and adjusts for exposure change due to PNL

    where :math:`cPNL(L/S)_{t-1}` is your performance reports cumulative long or short PNL at date t-1
    where :math:`PNL(L/S)_{t}` is your performance reports long or short pnl at date t
    where :math:`AUM_{t}` is portfolio exposure on date t
    where :math:`EXP(L/S)_{t}` is the long or short exposure on date t

    """
    start_date = DataContext.current.start_time - relativedelta(1)
    end_date = DataContext.current.end_time

    start_date = start_date.date()
    end_date = end_date.date()

    performance_report = PerformanceReport.get(report_id)
    if not aum_source:
        port = GsPortfolioApi.get_portfolio(
            performance_report.position_source_id)
        aum_source = port.aum_source if port.aum_source else RiskAumSource.Gross
    else:
        aum_source = RiskAumSource(aum_source)

    constituent_data = performance_report.get_portfolio_constituents(
        fields=['assetId', 'pnl', 'quantity', 'netExposure'],
        start_date=start_date,
        end_date=end_date).set_index('date')

    aum_col_name = aum_source.value.lower()
    aum_col_name = f'{aum_col_name}Exposure' if aum_col_name != 'custom aum' else 'aum'

    # Split into long and short and aggregate across dates
    long_side = _return_metrics(
        constituent_data[constituent_data['quantity'] > 0],
        list(constituent_data.index.unique()), "long")
    short_side = _return_metrics(
        constituent_data[constituent_data['quantity'] < 0],
        list(constituent_data.index.unique()), "short")
    # Get aum source data
    if aum_source == RiskAumSource.Custom_AUM:
        custom_aum = pd.DataFrame(
            GsPortfolioApi.get_custom_aum(
                performance_report.position_source_id, start_date, end_date))
        if custom_aum.empty:
            raise MqError(
                f'No custom AUM for portfolio {performance_report.position_source_id} between dates {start_date},'
                f' {end_date}')
        data = pd.DataFrame.from_records(custom_aum).set_index(['date'])
    else:
        data = performance_report.get_many_measures(
            [aum_col_name], start_date, end_date).set_index(['date'])

    long_side = long_side.join(data[[f'{aum_col_name}']], how='inner')
    short_side = short_side.join(data[[f'{aum_col_name}']], how='inner')

    long_side['longRetWeighted'] = (long_side['longMetrics'] - 1) * long_side['exposure'] * \
                                   (1 / long_side[f'{aum_col_name}'])
    short_side['shortRetWeighted'] = (short_side['shortMetrics'] - 1) * short_side['exposure'] *\
                                     (1 / short_side[f'{aum_col_name}'])

    combined = long_side[['longRetWeighted'
                          ]].join(short_side[['shortRetWeighted']],
                                  how='inner')
    combined['normalizedPerformance'] = combined['longRetWeighted'] + combined[
        'shortRetWeighted'] + 1
    return pd.Series(combined['normalizedPerformance'],
                     name="normalizedPerformance").dropna()
示例#15
0
from gs_quant.session import *
from gs_quant.target.reports import ReportStatus, PositionSourceType, ReportType, ReportParameters, Report

fake_pfr = FactorRiskReport(risk_model_id='AXUS4M',
                            fx_hedged=True,
                            report_id='PFRID',
                            position_source_type=PositionSourceType.Portfolio,
                            position_source_id='PORTFOLIOID',
                            report_type=ReportType.Portfolio_Factor_Risk,
                            status=ReportStatus.done
                            )

fake_ppa = PerformanceReport(report_id='PPAID',
                             position_source_type=PositionSourceType.Portfolio,
                             position_source_id='PORTFOLIOID',
                             report_type=ReportType.Portfolio_Performance_Analytics,
                             parameters=None,
                             status=ReportStatus.done
                             )
fake_pta = ThematicReport(report_id='PTAID',
                          position_source_type=PositionSourceType.Portfolio,
                          position_source_id='PORTFOLIOID',
                          report_type=ReportType.Portfolio_Thematic_Analytics,
                          parameters=None,
                          status=ReportStatus.done
                          )

factor_risk_results = [
    {
        'date': '2021-01-02',
        'factor': 'factor1',
示例#16
0
def normalized_performance(report_id: str,
                           aum_source: str = None,
                           *,
                           source: str = None,
                           real_time: bool = False,
                           request_id: Optional[str] = None) -> pd.Series:
    """
    Returns the Normalized Performance of a performance report based on AUM source
    :param report_id: id of performance report
    :param aum_source: source to normalize pnl from, default is the aum source on your portfolio,
                if no aum source is set on your portfolio the default is gross
    :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 normalized performance

    **Usage**

    Returns the normalized performance of the portfolio based on AUM source.

    If :math:`aum_source` is "Custom AUM":
    We read AUM from custom AUM uploaded to that portfolio and normalize performance based on that exposure

    If :math:`aum_source` is one of: Long, Short, RiskAumSource.Net,
        AumSource.Gross, we take these exposures from the calculated exposures based on daily positions

    :math:`NP_{t} = SUM( PNL_{t}/ ( AUM_{t} ) - cPNL_{t-1) ) if ( AUM_{t} ) - cPNL_{t-1) ) > 0 else:
            1/ SUM( PNL_{t}/ ( AUM_{t} ) - cPNL_{t-1) )`

    This takes into account varying AUM and adjusts for exposure change due to PNL

    where :math:`cPNL_{t-1}` is your performance reports cumulative PNL at date t-1
    where :math:`PNL_{t}` is your performance reports pnl at date t
    where :math:`AUM_{t}` is portfolio exposure on date t


    """
    start_date = DataContext.current.start_date
    end_date = DataContext.current.end_date

    ppa_report = PerformanceReport.get(report_id)
    if not aum_source:
        port = GsPortfolioApi.get_portfolio(ppa_report.position_source_id)
        aum_source = port.aum_source if port.aum_source else RiskAumSource.Net
    else:
        aum_source = RiskAumSource(aum_source)

    aum_col_name = aum_source.value.lower()
    aum_col_name = f'{aum_col_name}Exposure' if aum_col_name != 'custom aum' else 'aum'
    measures = [aum_col_name, 'pnl'
                ] if aum_source != RiskAumSource.Custom_AUM else ['pnl']
    data = ppa_report.get_many_measures(measures, start_date, end_date)
    data.loc[0, 'pnl'] = 0
    data['cumulativePnlT-1'] = data['pnl'].cumsum(axis=0)
    data = pd.DataFrame.from_records(data).set_index(['date'])
    if aum_source == RiskAumSource.Custom_AUM:
        custom_aum = pd.DataFrame(
            GsPortfolioApi.get_custom_aum(ppa_report.position_source_id,
                                          start_date, end_date))
        if custom_aum.empty:
            raise MqError(
                f'No custom AUM for portfolio {ppa_report.position_source_id} between dates {start_date},'
                f' {end_date}')
        custom_aum = pd.DataFrame.from_records(custom_aum).set_index(['date'])
        data = data.join(custom_aum.loc[:, aum_col_name], how='inner')
    if aum_source == RiskAumSource.Short:
        data[f'{aum_col_name}'] = -1 * data[f'{aum_col_name}']
    data['normalizedExposure'] = data[f'{aum_col_name}'] - data[
        'cumulativePnlT-1']
    data[
        'pnlOverNormalizedExposure'] = data['pnl'] / data['normalizedExposure']
    data['normalizedPerformance'] = data['pnlOverNormalizedExposure'].cumsum(
        axis=0) + 1
    data.loc[data.normalizedExposure < 0,
             'normalizedPerformance'] = 1 / data.loc[:,
                                                     'normalizedPerformance']
    return pd.Series(data['normalizedPerformance'],
                     name="normalizedPerformance").dropna()
示例#17
0
def test_normalized_performance():
    idx = pd.date_range('2020-01-02', freq='D', periods=3)
    expected = {
        RiskAumSource.Net:
        pd.Series(data=[1, 1 + 2 / 4, 1 + 6 / 6],
                  index=idx,
                  name='normalizedPerformance',
                  dtype='float64'),
        RiskAumSource.Gross:
        pd.Series(data=[1, 1 + 2 / 1.2, 1 + 6 / 1.3],
                  index=idx,
                  name='normalizedPerformance',
                  dtype='float64'),
        RiskAumSource.Long:
        pd.Series(data=[1, 1 + 2 / 1.2, 1 + 6 / 1.3],
                  index=idx,
                  name='normalizedPerformance',
                  dtype='float64'),
        RiskAumSource.Short:
        pd.Series(data=[1, 1 + 2 / 1.2, 1 + 6 / 1.3],
                  index=idx,
                  name='normalizedPerformance',
                  dtype='float64'),
        RiskAumSource.Custom_AUM:
        pd.Series(data=[1, 1 + 2 / 2.2, 1 + 6 / 2.4],
                  index=idx,
                  name='normalizedPerformance',
                  dtype='float64')
    }

    with DataContext(datetime.date(2020, 1, 1), datetime.date(2019, 1, 3)):
        for k, v in expected.items():
            df = MarketDataResponseFrame(data=ppa_data, dtype="float64")
            replace = Replacer()

            # mock GsPortfolioApi.get_reports()
            mock = replace(
                'gs_quant.api.gs.portfolios.GsPortfolioApi.get_reports',
                Mock())
            mock.return_value = [
                Report.from_dict({
                    'id': 'RP1',
                    'positionSourceType': 'Portfolio',
                    'positionSourceId': 'MP1',
                    'type': 'Portfolio Performance Analytics',
                    'parameters': {
                        'transactionCostModel': 'FIXED'
                    }
                })
            ]
            # mock PerformanceReport.get_portfolio_constituents()
            mock = replace(
                'gs_quant.markets.report.PerformanceReport.get_portfolio_constituents',
                Mock())
            mock.return_value = MarketDataResponseFrame(data=constituents_data,
                                                        dtype="float64")

            # mock PerformanceReport.get_many_measures()
            mock = replace(
                'gs_quant.markets.report.PerformanceReport.get_many_measures',
                Mock())
            mock.return_value = df

            # mock PerformanceReport.get_custom_aum()
            mock = replace(
                'gs_quant.api.gs.portfolios.GsPortfolioApi.get_custom_aum',
                Mock())
            mock.return_value = aum

            # mock PerformanceReport.get()
            mock = replace('gs_quant.markets.report.PerformanceReport.get',
                           Mock())
            mock.return_value = PerformanceReport(
                report_id='RP1',
                position_source_type='Portfolio',
                position_source_id='MP1',
                report_type='Portfolio Performance Analytics',
                parameters=ReportParameters(transaction_cost_model='FIXED'))

            actual = mr.normalized_performance('MP1', k.value)
            assert all(actual.values == v.values)
            replace.restore()
示例#18
0
def normalized_performance(report_id: str, leg: str = None, *, source: str = None,
                           real_time: bool = False, request_id: Optional[str] = None) -> pd.Series:
    """
    Returns the Normalized Performance of a performance report based on AUM source
    :param report_id: id of performance report
    :param leg: short or long
    :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 normalized performance

    **Usage**

    Returns the normalized performance of the portfolio.

    :math:`NP(L/S)_{t} = SUM( PNL(L/S)_{t}/ ( EXP(L/S)_{t} ) - cPNL(L/S)_{t-1) )
        if ( EXP(L/S)_{t} ) > 0
        else:
            1/ SUM( PNL(L/S)_{t}/ ( EXP(L/S)_{t} ) - cPNL(L/S)_{t-1) )`
    For each leg, short and long, then:
    :math:`NP_{t} = NP(L)_{t} * SUM(EXP(L)) / SUM(GROSS_EXP) + NP(S)_{t} * SUM(EXP(S)) / SUM(GROSS_EXP) + 1`

    If leg is short, set SUM(EXP(L)) to 0, if leg is long, set SUM(EXP(S)) to 0

    where :math:`cPNL(L/S)_{t-1}` is your performance reports cumulative long or short PNL at date t-1
    where :math:`PNL(L/S)_{t}` is your performance reports long or short pnl at date t
    where :math:`GROSS_EXP_{t}` is portfolio gross exposure on date t
    where :math:`EXP(L/S)_{t}` is the long or short exposure on date t

    """
    start_date = DataContext.current.start_time
    end_date = DataContext.current.end_time

    start_date = (start_date - BDay(1)).date()
    end_date = end_date.date()

    performance_report = PerformanceReport.get(report_id)

    constituent_data = performance_report.get_portfolio_constituents(
        fields=['assetId', 'pnl', 'quantity', 'netExposure'], start_date=start_date, end_date=end_date).set_index(
        'date')

    if leg:
        if leg.lower() == "long":
            constituent_data = constituent_data[constituent_data['quantity'] > 0]
        if leg.lower() == "short":
            constituent_data = constituent_data[constituent_data['quantity'] < 0]

    # Split into long and short and aggregate across dates
    long_side = _return_metrics(constituent_data[constituent_data['quantity'] > 0],
                                list(constituent_data.index.unique()), "long")
    short_side = _return_metrics(constituent_data[constituent_data['quantity'] < 0],
                                 list(constituent_data.index.unique()), "short")

    short_exposure = sum(abs(short_side['exposure']))
    long_exposure = sum(long_side['exposure'])
    gross_exposure = short_exposure + long_exposure

    long_side['longRetWeighted'] = (long_side['longMetrics'] - 1) * (long_exposure / gross_exposure)
    short_side['shortRetWeighted'] = (short_side['shortMetrics'] - 1) * (short_exposure / gross_exposure)

    combined = long_side[['longRetWeighted']].join(short_side[['shortRetWeighted']], how='inner')
    combined['normalizedPerformance'] = combined['longRetWeighted'] + combined['shortRetWeighted'] + 1
    return pd.Series(combined['normalizedPerformance'], name="normalizedPerformance").dropna()