Skip to content

Commit

Permalink
HAPI data provider - ticker with special characters support (#117)
Browse files Browse the repository at this point in the history
* Supporting Bloomberg tickers with special characters

* Added unit tests

---------

Co-authored-by: Karolina Cynk <[email protected]>
  • Loading branch information
myrmarachne and Karolina Cynk authored Jun 7, 2023
1 parent e371100 commit 4303110
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import re
from typing import Union, Sequence, Optional, List, Tuple
import pprint
from urllib.parse import urljoin
Expand Down Expand Up @@ -64,7 +64,7 @@ def get_universe_url(self, universe_id: str, tickers: Union[str, Sequence[str]],
URL address of created hapi universe
"""
tickers, got_single_field = convert_to_list(tickers, str)
tickers_and_types = [self._get_indentifier_and_type(ticker) for ticker in tickers]
tickers_and_types = [self._get_identifier_and_type(ticker) for ticker in tickers]
contains = [{'@type': 'Identifier', 'identifierType': identifier_type, 'identifierValue': identifier}
for identifier_type, identifier in tickers_and_types if identifier]
if len(contains) == 0:
Expand Down Expand Up @@ -108,7 +108,7 @@ def get_universe_url(self, universe_id: str, tickers: Union[str, Sequence[str]],

return universe_url

def _get_indentifier_and_type(self, ticker) -> Tuple[Optional[str], Optional[str]]:
def _get_identifier_and_type(self, identifier: str) -> Tuple[Optional[str], Optional[str]]:
blp_hapi_compatibility_mapping = {
"ticker": "TICKER",
"cusip": "CUSIP",
Expand All @@ -122,15 +122,16 @@ def _get_indentifier_and_type(self, ticker) -> Tuple[Optional[str], Optional[str
"cats": "CATS"
}

ticker = f"/ticker/{ticker}" if ticker.count("/") == 0 else ticker
if ticker.count("/") != 2:
self.logger.error(f"Detected incorrect identifier: {ticker}. It will be removed from the data request.\n"
identifier = f"/ticker/{identifier}" if not identifier.startswith("/") else identifier
match = re.match(r"^/(\w+)/(.+)", identifier)
if not match:
self.logger.error(f"Detected incorrect identifier: {identifier}. It will be removed from the data request.\n"
f"In order to provide an identifier, which is not a ticker, please use "
f"'/id_type/identifier' format, with id_type being one of the following: "
f"{blp_hapi_compatibility_mapping.values()}")
return None, None

id_type, id = ticker.lstrip("/").split("/")
id_type, id = match.groups()
try:
return blp_hapi_compatibility_mapping[id_type.lower()], id
except KeyError:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,33 +31,60 @@ def setUp(self):
self.location = '{}universes/{}/'.format(self.address_url, self.fieldlist_id)
self.host = 'https://api.bloomberg.com'
self.account_url = urljoin(self.host, self.address_url)
self.tickers = ['TICKER']
self.post_response.headers = {'Location': self.location}

def test_get_fields_url__get_response(self):
def test_get_universe_url__get_response(self):
self.session_mock.get.return_value.status_code = 200
provider = BloombergBeapHapiUniverseProvider(self.host, self.session_mock, self.account_url)
field_overrides = [("CHAIN_DATE", datetime.now().strftime('%Y%m%d')), ('INCLUDE_EXPIRED_CONTRACTS', 'Y')]
url = provider.get_universe_url(self.fieldlist_id, self.tickers, field_overrides)
tickers = ["SPY US Equity"]
url = provider.get_universe_url(self.fieldlist_id, tickers, field_overrides)
self.assertEqual(url, urljoin(self.host, self.location))

def test_get_fields_url__post_response(self):
def test_get_universe_url__post_response(self):
self.session_mock.get.return_value.status_code = 404
self.post_response.status_code = 201
provider = BloombergBeapHapiUniverseProvider(self.host, self.session_mock, self.account_url)
field_overrides = [("CHAIN_DATE", datetime.now().strftime('%Y%m%d')), ('INCLUDE_EXPIRED_CONTRACTS', 'Y')]
url = provider.get_universe_url(self.fieldlist_id, self.tickers, field_overrides)
tickers = ["SPY US Equity"]
url = provider.get_universe_url(self.fieldlist_id, tickers, field_overrides)
self.assertEqual(url, urljoin(self.host, self.location))

def test_get_fields_url__unknown_get_response(self):
def test_get_universe_url__unknown_get_response(self):
self.session_mock.get.return_value.status_code = 404
provider = BloombergBeapHapiUniverseProvider(self.host, self.session_mock, self.account_url)
field_overrides = [("CHAIN_DATE", datetime.now().strftime('%Y%m%d')), ('INCLUDE_EXPIRED_CONTRACTS', 'Y')]
self.assertRaises(BloombergError, provider.get_universe_url, self.fieldlist_id, self.tickers, field_overrides)
tickers = ["SPY US Equity"]
self.assertRaises(BloombergError, provider.get_universe_url, self.fieldlist_id, tickers, field_overrides)

def test_get_fields_url__unknown_post_response(self):
def test_get_universe_url__unknown_post_response(self):
self.session_mock.get.return_value.status_code = 404
self.post_response.status_code = 200
provider = BloombergBeapHapiUniverseProvider(self.host, self.session_mock, self.account_url)
field_overrides = [("CHAIN_DATE", datetime.now().strftime('%Y%m%d')), ('INCLUDE_EXPIRED_CONTRACTS', 'Y')]
self.assertRaises(BloombergError, provider.get_universe_url, self.fieldlist_id, self.tickers, field_overrides)
tickers = ["SPY US Equity"]
self.assertRaises(BloombergError, provider.get_universe_url, self.fieldlist_id, tickers, field_overrides)

def test_get_universe_url__no_correct_identifiers(self):
provider = BloombergBeapHapiUniverseProvider(self.host, self.session_mock, self.account_url)
tickers = ["/incorrect_type/SPY US Equity"]
self.assertRaises(ValueError, provider.get_universe_url, self.fieldlist_id, tickers)

def test_get_universe_url__empty_identifiers_list(self):
provider = BloombergBeapHapiUniverseProvider(self.host, self.session_mock, self.account_url)
tickers = []
self.assertRaises(ValueError, provider.get_universe_url, self.fieldlist_id, tickers)

def test_get_universe_url__correct_identifier_with_special_characters(self):
self.session_mock.get.return_value.status_code = 200
provider = BloombergBeapHapiUniverseProvider(self.host, self.session_mock, self.account_url)
tickers = ["/ticker/AA/ US Equity"]
url = provider.get_universe_url(self.fieldlist_id, tickers)
self.assertEqual(url, urljoin(self.host, self.location))

def test_get_universe_url__correct_ticker_with_special_characters(self):
self.session_mock.get.return_value.status_code = 200
provider = BloombergBeapHapiUniverseProvider(self.host, self.session_mock, self.account_url)
tickers = ["AA/ US Equity"]
url = provider.get_universe_url(self.fieldlist_id, tickers)
self.assertEqual(url, urljoin(self.host, self.location))

0 comments on commit 4303110

Please sign in to comment.