From f91ba6c5f56b2246a46fe50f9d62553216505ba7 Mon Sep 17 00:00:00 2001 From: avantgardeam Date: Fri, 16 Jun 2023 00:04:00 -0300 Subject: [PATCH 1/3] add support beqs --- src/blp/blp.py | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/blp/blp.py b/src/blp/blp.py index c27c405..f94294b 100644 --- a/src/blp/blp.py +++ b/src/blp/blp.py @@ -489,6 +489,7 @@ class BlpQuery(BlpSession): "ReferenceDataRequest": "//blp/refdata", "IntradayTickRequest": "//blp/refdata", "IntradayBarRequest": "//blp/refdata", + "BeqsRequest": "//blp/refdata", "FieldInfoRequest": "//blp/apiflds", "FieldListRequest": "//blp/apiflds", "instrumentListRequest": "//blp/instruments", @@ -802,6 +803,45 @@ def collect_to_bdh(self, responses: Iterable) -> Dict[str, pandas.DataFrame]: dfs[sec] = df_list[0] return dfs + + def beqs( + self, + screen_name: str, + screen_type: str = "PRIVATE", + overrides: Optional[Sequence] = None, + options: Optional[Dict] = None, + ) -> pandas.DataFrame: + """ Bloomberg equity screening request. + + Args: + screen_name: name of the screen + screen_type: type of screen, either 'PRIVATE' or 'GLOBAL' + overrides: List of tuples containing the field to override and its value + options: key value pairs to to set in request + + Returns: A pandas.DataFrame with columns ['security', eqs_data[0], ...] + """ + query = create_eqs_query(screen_name, screen_type, overrides, options) + df = self.query(query, self.parser, self.collect_to_beqs) + columns = ["security"] + sorted([col for col in df.columns if col != "security"]) + df = df.sort_values("security")\ + .reset_index(drop=True)\ + .loc[:, columns]\ + .drop(['Ticker'], axis=1) # Ticker = security + return df + + def collect_to_beqs(self, responses: Iterable) -> pandas.DataFrame: + """Collector for beqs().""" + rows = [] + fields = {"security"} + for response in responses: + data = response["data"] + # possible some fields are missing for different securities + fields = fields.union(set(response["fields"])) + data["security"] = response["security"] + rows.append(data) + df = pandas.DataFrame(rows) + return self.cast_columns(df, fields) def bdp( self, @@ -1070,6 +1110,7 @@ def _validate_response_type(response, _): "ReferenceDataResponse", "HistoricalDataResponse", "IntradayBarResponse", + "BeqsResponse", "IntradayTickResponse", "fieldResponse", "InstrumentListResponse", @@ -1093,6 +1134,7 @@ def _process_field_exception(response, _): if rtype in ( "IntradayBarResponse", "IntradayTickResponse", + "BeqsResponse", "fieldResponse", "InstrumentListResponse", "GetFillsResponse", @@ -1135,6 +1177,7 @@ def _validate_security_error(response, _): if rtype in ( "IntradayBarResponse", "IntradayTickResponse", + "BeqsResponse", "fieldResponse", "InstrumentListResponse", "GetFillsResponse", @@ -1155,6 +1198,7 @@ def _warn_security_error(response, _): if rtype in ( "IntradayBarResponse", "IntradayTickResponse", + "BeqsResponse", "fieldResponse", "InstrumentListResponse", "GetFillsResponse", @@ -1293,6 +1337,8 @@ def __call__(self, response, request_data): sec_data_parser = self._parse_bar_security_data elif rtype == "IntradayTickResponse": sec_data_parser = self._parse_tick_security_data + elif rtype == "BeqsResponse": + sec_data_parser = self._parse_equity_screening_data elif rtype == "fieldResponse": sec_data_parser = self._parse_field_info_data elif rtype == "InstrumentListResponse": @@ -1305,6 +1351,7 @@ def __call__(self, response, request_data): "HistoricalDataResponse", "IntradayBarResponse", "IntradayTickResponse", + "BeqsResponse", "fieldResponse", "InstrumentListResponse", "GetFillsResponse", @@ -1388,6 +1435,20 @@ def _parse_tick_security_data(response, request_data): } yield result + @staticmethod + def _parse_equity_screening_data(response, _): + rtype = list(response["message"]["element"].keys())[0] + response_data = response["message"]["element"][rtype]['data'] + fields = list(response_data["fieldDisplayUnits"]["fieldDisplayUnits"].keys()) + + for sec_data in response_data["securityData"]: + result = { + "security": sec_data["securityData"]["security"], + "fields": fields, + "data": sec_data["securityData"]["fieldData"]["fieldData"], + } + yield result + @staticmethod def _parse_field_info_data(response, request_data): rtype = "fieldResponse" @@ -1472,6 +1533,29 @@ def create_query(request_type: str, values: Dict, overrides: Optional[Sequence] request_dict[request_type]["overrides"] = ovrds return request_dict +def create_eqs_query( + screen_name: str, + screen_type: str = "PRIVATE", + overrides: Optional[Sequence] = None, + options: Optional[Dict] = None, +) -> Dict: + """Create a BeqsRequest dictionary request. + + Args: + screen_name: name of the screen + screen_type: type of screen; either PRIVATE or GLOBAL + overrides: List of tuples containing the field to override and its value + options: key value pairs to to set in request + + Returns: A dictionary representation of a blpapi.Request + """ + values = { + "screenName": screen_name, + "screenType": screen_type, + } + if options: + values.update(options) + return create_query("BeqsRequest", values, overrides) def create_historical_query( securities: Union[str, Sequence[str]], From 15352f2aef7446bf81d0bbdda4d9d0b1ad4817b5 Mon Sep 17 00:00:00 2001 From: Matthew Date: Fri, 16 Jun 2023 06:20:38 -0400 Subject: [PATCH 2/3] ENH: Black code formatting --- src/blp/blp.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/blp/blp.py b/src/blp/blp.py index f94294b..f9f544a 100644 --- a/src/blp/blp.py +++ b/src/blp/blp.py @@ -38,11 +38,7 @@ _TOKEN_FAILURE = blpapi.Name("TokenGenerationFailure") _SESSION_TERMINATED = blpapi.Name("SessionTerminated") _SESSION_DOWN = blpapi.Name("SessionConnectionDown") -_MARKET_DATA_EVENTS = [ - blpapi.Name("MarketDataEvents"), - blpapi.Name("MarketBarStart"), - blpapi.Name("MarketBarUpdate") -] +_MARKET_DATA_EVENTS = [blpapi.Name("MarketDataEvents"), blpapi.Name("MarketBarStart"), blpapi.Name("MarketBarUpdate")] logger = logging.getLogger(__name__) @@ -803,7 +799,7 @@ def collect_to_bdh(self, responses: Iterable) -> Dict[str, pandas.DataFrame]: dfs[sec] = df_list[0] return dfs - + def beqs( self, screen_name: str, @@ -811,23 +807,22 @@ def beqs( overrides: Optional[Sequence] = None, options: Optional[Dict] = None, ) -> pandas.DataFrame: - """ Bloomberg equity screening request. + """Bloomberg equity screening request. Args: screen_name: name of the screen screen_type: type of screen, either 'PRIVATE' or 'GLOBAL' overrides: List of tuples containing the field to override and its value options: key value pairs to to set in request - + Returns: A pandas.DataFrame with columns ['security', eqs_data[0], ...] """ query = create_eqs_query(screen_name, screen_type, overrides, options) df = self.query(query, self.parser, self.collect_to_beqs) columns = ["security"] + sorted([col for col in df.columns if col != "security"]) - df = df.sort_values("security")\ - .reset_index(drop=True)\ - .loc[:, columns]\ - .drop(['Ticker'], axis=1) # Ticker = security + df = ( + df.sort_values("security").reset_index(drop=True).loc[:, columns].drop(["Ticker"], axis=1) + ) # Ticker = security return df def collect_to_beqs(self, responses: Iterable) -> pandas.DataFrame: @@ -1438,7 +1433,7 @@ def _parse_tick_security_data(response, request_data): @staticmethod def _parse_equity_screening_data(response, _): rtype = list(response["message"]["element"].keys())[0] - response_data = response["message"]["element"][rtype]['data'] + response_data = response["message"]["element"][rtype]["data"] fields = list(response_data["fieldDisplayUnits"]["fieldDisplayUnits"].keys()) for sec_data in response_data["securityData"]: @@ -1533,6 +1528,7 @@ def create_query(request_type: str, values: Dict, overrides: Optional[Sequence] request_dict[request_type]["overrides"] = ovrds return request_dict + def create_eqs_query( screen_name: str, screen_type: str = "PRIVATE", @@ -1557,6 +1553,7 @@ def create_eqs_query( values.update(options) return create_query("BeqsRequest", values, overrides) + def create_historical_query( securities: Union[str, Sequence[str]], fields: Union[str, Sequence[str]], From 3dbb3defd17412abadd9c951616d70af7c6b6ed5 Mon Sep 17 00:00:00 2001 From: avantgardeam Date: Tue, 20 Jun 2023 00:45:02 -0300 Subject: [PATCH 3/3] Change beqs column structure. First, keep "Ticker" column instead of dropping it. Second, don't sort columns on alphabetical order. --- src/blp/blp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/blp/blp.py b/src/blp/blp.py index f9f544a..e4e8e45 100644 --- a/src/blp/blp.py +++ b/src/blp/blp.py @@ -819,9 +819,9 @@ def beqs( """ query = create_eqs_query(screen_name, screen_type, overrides, options) df = self.query(query, self.parser, self.collect_to_beqs) - columns = ["security"] + sorted([col for col in df.columns if col != "security"]) + columns = ["security"] + [col for col in df.columns if col != "security"] df = ( - df.sort_values("security").reset_index(drop=True).loc[:, columns].drop(["Ticker"], axis=1) + df.sort_values("security").reset_index(drop=True).loc[:, columns] ) # Ticker = security return df