Beispiel #1
0
 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
Beispiel #2
0
 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
Beispiel #3
0
 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)
Beispiel #4
0
 def from_portfolio_id(portfolio_id: str):
     portfolio = GsPortfolioApi.get_portfolio(portfolio_id)
     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, name=portfolio.name)
     ret.__id = portfolio_id
     return ret
Beispiel #5
0
 def set_aum_source(self, aum_source: RiskAumSource):
     """
     Set portfolio AUM Source
     :param aum_source: aum source for portfolio
     :return:
     """
     portfolio = GsPortfolioApi.get_portfolio(self.portfolio_id)
     portfolio.aum_source = aum_source
     GsPortfolioApi.update_portfolio(portfolio)
Beispiel #6
0
 def set_entitlements(self, entitlements: Entitlements):
     """
     Set the entitlements of a portfolio
     :param entitlements: Entitlements object
     """
     entitlements_as_target = entitlements.to_target()
     portfolio_as_target = GsPortfolioApi.get_portfolio(self.__portfolio_id)
     portfolio_as_target.entitlements = entitlements_as_target
     GsPortfolioApi.update_portfolio(portfolio_as_target)
Beispiel #7
0
 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
Beispiel #8
0
 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
Beispiel #9
0
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
Beispiel #10
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()
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()