def market_normal(self, session, after_open, before_close) -> Session: """ Time intervals between market Args: session: [allday, day, am, pm, night] after_open: mins after open before_close: mins before close Returns: Session of start_time and end_time """ logger = logs.get_logger(self.market_normal) if session not in self.exch: return SessNA ss = self.exch[session] s_time = shift_time(ss[0], int(after_open) + 1) e_time = shift_time(ss[-1], -int(before_close)) request_cross = pd.Timestamp(s_time) >= pd.Timestamp(e_time) session_cross = pd.Timestamp(ss[0]) >= pd.Timestamp(ss[1]) if request_cross and (not session_cross): logger.warning(f'end time {e_time} is earlier than {s_time} ...') return SessNA return Session(s_time, e_time)
def subscribe(tickers, flds=None, identity=None, options=None, **kwargs): """ Subscribe Bloomberg realtime data Args: tickers: list of tickers flds: fields to subscribe, default: Last_Price, Bid, Ask identity: Bloomberg identity """ logger = logs.get_logger(subscribe, **kwargs) if isinstance(tickers, str): tickers = [tickers] if flds is None: flds = ['Last_Price', 'Bid', 'Ask'] if isinstance(flds, str): flds = [flds] sub_list = conn.blpapi.SubscriptionList() for ticker in tickers: topic = f'//blp/mktdata/{ticker}' cid = conn.blpapi.CorrelationId(ticker) logger.debug(f'Subscribing {cid} => {topic}') sub_list.add(topic, flds, correlationId=cid, options=options) try: conn.bbg_session(**kwargs).subscribe(sub_list, identity) yield finally: conn.bbg_session(**kwargs).unsubscribe(sub_list)
def latest_file(path_name, keyword='', ext='', **kwargs) -> str: """ Latest modified file in folder Args: path_name: full path name keyword: keyword to search ext: file extension Returns: str: latest file name """ files = all_files(path_name=path_name, keyword=keyword, ext=ext, full_path=True) if not files: from xbbg.io import logs logger = logs.get_logger(latest_file, level=kwargs.pop('log', 'warning')) logger.debug(f'file is not found in folder: {path_name}') return '' modified_time = [os.path.getmtime(f) for f in files] files = [f for (dt, f) in sorted(zip(modified_time, files))] return files[-1]
def bbg_service(service: str, **kwargs) -> blpapi.service.Service: """ Initiate service Args: service: service name **kwargs: port: port number Returns: Bloomberg service """ logger = logs.get_logger(bbg_service, **kwargs) port = kwargs.get('port', _PORT_) serv_sym = f'{_CON_SYM_}/{port}{service}' log_info = f'Initiating service {service} ...' if serv_sym in globals(): if getattr(globals()[serv_sym], '_Service__handle', None) is None: log_info = f'Restarting service {service} ...' del globals()[serv_sym] if serv_sym not in globals(): logger.debug(log_info) bbg_session(**kwargs).openService(service) globals()[serv_sym] = bbg_session(**kwargs).getService(service) return globals()[serv_sym]
def tz_convert(dt, to_tz, from_tz=None) -> str: """ Convert to tz Args: dt: date time to_tz: to tz from_tz: from tz - will be ignored if tz from dt is given Returns: str: date & time Examples: >>> dt_1 = pd.Timestamp('2018-09-10 16:00', tz='Asia/Hong_Kong') >>> tz_convert(dt_1, to_tz='NY') '2018-09-10 04:00:00-04:00' >>> dt_2 = pd.Timestamp('2018-01-10 16:00') >>> tz_convert(dt_2, to_tz='HK', from_tz='NY') '2018-01-11 05:00:00+08:00' >>> dt_3 = '2018-09-10 15:00' >>> tz_convert(dt_3, to_tz='NY', from_tz='JP') '2018-09-10 02:00:00-04:00' """ logger = logs.get_logger(tz_convert, level='debug') f_tz, t_tz = get_tz(from_tz), get_tz(to_tz) from_dt = pd.Timestamp(str(dt), tz=f_tz) logger.debug(f'converting {str(from_dt)} from {f_tz} to {t_tz} ...') return str(pd.Timestamp(str(from_dt), tz=t_tz))
def bdh(tickers, flds=None, start_date=None, end_date='today', adjust=None, **kwargs) -> pd.DataFrame: """ Bloomberg historical data Args: tickers: ticker(s) flds: field(s) start_date: start date end_date: end date - default today adjust: `all`, `dvd`, `normal`, `abn` (=abnormal), `split`, `-` or None exact match of above words will adjust for corresponding events Case 0: `-` no adjustment for dividend or split Case 1: `dvd` or `normal|abn` will adjust for all dividends except splits Case 2: `adjust` will adjust for splits and ignore all dividends Case 3: `all` == `dvd|split` == adjust for all Case 4: None == Bloomberg default OR use kwargs **kwargs: overrides Returns: pd.DataFrame """ logger = logs.get_logger(bdh, **kwargs) if flds is None: flds = ['Last_Price'] e_dt = utils.fmt_dt(end_date, fmt='%Y%m%d') if start_date is None: start_date = pd.Timestamp(e_dt) - pd.Timedelta(weeks=8) s_dt = utils.fmt_dt(start_date, fmt='%Y%m%d') request = process.create_request( service='//blp/refdata', request='HistoricalDataRequest', **kwargs, ) process.init_request(request=request, tickers=tickers, flds=flds, start_date=s_dt, end_date=e_dt, adjust=adjust, **kwargs) logger.debug(f'Sending request to Bloomberg ...\n{request}') conn.send_request(request=request, **kwargs) res = pd.DataFrame(process.rec_events(process.process_hist, **kwargs)) if kwargs.get('raw', False): return res if res.empty or any(fld not in res for fld in ['ticker', 'date']): return pd.DataFrame() return (res.set_index(['ticker', 'date']).unstack(level=0).rename_axis( index=None, columns=[None, None]).swaplevel( 0, 1, axis=1).reindex(columns=utils.flatten(tickers), level=0).reindex(columns=utils.flatten(flds), level=1))
def fut_ticker(gen_ticker: str, dt, freq: str, log=logs.LOG_LEVEL): """ Get proper ticker from generic ticker Args: gen_ticker: generic ticker dt: date freq: futures contract frequency log: level of logs Returns: str: exact futures ticker """ logger = logs.get_logger(fut_ticker, level=log) dt = pd.Timestamp(dt) t_info = gen_ticker.split() asset = t_info[-1] if asset in ['Index', 'Curncy', 'Comdty']: ticker = ' '.join(t_info[:-1]) prefix, idx, postfix = ticker[:-1], int(ticker[-1]) - 1, asset elif asset == 'Equity': ticker = t_info[0] prefix, idx, postfix = ticker[:-1], int(ticker[-1]) - 1, ' '.join(t_info[1:]) else: logger.error(f'unkonwn asset type for ticker: {gen_ticker}') return '' month_ext = 4 if asset == 'Comdty' else 2 months = pd.DatetimeIndex(start=dt, periods=max(idx + month_ext, 3), freq=freq) logger.debug(f'pulling expiry dates for months: {months}') def to_fut(month): return prefix + const.Futures[month.strftime('%b')] + \ month.strftime('%y')[-1] + ' ' + postfix fut = [to_fut(m) for m in months] logger.debug(f'trying futures: {fut}') # noinspection PyBroadException try: fut_matu = bdp(tickers=fut, flds='last_tradeable_dt', cache=True) except Exception as e1: logger.error(f'error downloading futures contracts (1st trial) {e1}:\n{fut}') # noinspection PyBroadException try: fut = fut[:-1] logger.debug(f'trying futures (2nd trial): {fut}') fut_matu = bdp(tickers=fut, flds='last_tradeable_dt', cache=True) except Exception as e2: logger.error(f'error downloading futures contracts (2nd trial) {e2}:\n{fut}') return '' sub_fut = fut_matu[pd.DatetimeIndex(fut_matu.value) > dt] logger.debug(f'futures full chain:\n{fut_matu.to_string()}') logger.debug(f'getting index {idx} from:\n{sub_fut.to_string()}') return sub_fut.ticker.values[idx]
def exch_info(ticker: str) -> pd.Series: """ Exchange info for given ticker Args: ticker: ticker or exchange Returns: pd.Series Examples: >>> exch_info('SPY US Equity') tz America/New_York allday [04:00, 20:00] day [09:30, 16:00] pre [04:00, 09:30] post [16:01, 20:00] dtype: object >>> exch_info('ES1 Index') tz America/New_York allday [18:00, 17:00] day [08:00, 17:00] dtype: object >>> exch_info('Z 1 Index') tz Europe/London allday [01:00, 21:00] day [01:00, 21:00] dtype: object >>> exch_info('TESTTICKER Corp').empty True >>> exch_info('US') tz America/New_York allday [04:00, 20:00] day [09:30, 16:00] pre [04:00, 09:30] post [16:01, 20:00] dtype: object """ logger = logs.get_logger(exch_info, level='debug') if ' ' not in ticker.strip(): ticker = f'XYZ {ticker.strip()} Equity' info = param.load_info(cat='exch').get( market_info(ticker=ticker).get('exch', ''), dict()) if ('allday' in info) and ('day' not in info): info['day'] = info['allday'] if any(req not in info for req in ['tz', 'allday', 'day']): logger.error(f'required exchange info cannot be found in {ticker} ...') return pd.Series() for ss in ValidSessions: if ss not in info: continue info[ss] = [param.to_hour(num=s) for s in info[ss]] return pd.Series(info)
def beqs(screen, asof=None, typ='PRIVATE', group='General', **kwargs) -> pd.DataFrame: """ Bloomberg equity screening Args: screen: screen name asof: as of date typ: GLOBAL/B (Bloomberg) or PRIVATE/C (Custom, default) group: group name if screen is organized into groups Returns: pd.DataFrame """ logger = logs.get_logger(beqs, **kwargs) service = conn.bbg_service(service='//blp/refdata', **kwargs) request = service.createRequest('BeqsRequest') request.set('screenName', screen) request.set('screenType', 'GLOBAL' if typ[0].upper() in ['G', 'B'] else 'PRIVATE') request.set('Group', group) if asof: overrides = request.getElement('overrides') ovrd = overrides.appendElement() ovrd.setElement('fieldId', 'PiTDate') ovrd.setElement('value', utils.fmt_dt(asof, '%Y%m%d')) logger.debug(f'Sending request to Bloomberg ...\n{request}') conn.send_request(request=request, **kwargs) res = pd.DataFrame(process.rec_events(func=process.process_ref, **kwargs)) if res.empty: if kwargs.get('trial', 0): return pd.DataFrame() else: return beqs(screen=screen, asof=asof, typ=typ, group=group, trial=1, **kwargs) if kwargs.get('raw', False): return res cols = res.field.unique() return (res.set_index(['ticker', 'field']).unstack(level=1).rename_axis( index=None, columns=[None, None ]).droplevel(axis=1, level=0).loc[:, cols].pipe(pipeline.standard_cols))
def bds(tickers, flds, **kwargs) -> pd.DataFrame: """ Bloomberg block data Args: tickers: ticker(s) flds: field **kwargs: other overrides for query Returns: pd.DataFrame: block data """ logger = logs.get_logger(bds, **kwargs) service = conn.bbg_service(service='//blp/refdata', **kwargs) request = service.createRequest('ReferenceDataRequest') if isinstance(tickers, str): data_file = storage.ref_file(ticker=tickers, fld=flds, has_date=True, ext='pkl', **kwargs) if files.exists(data_file): logger.debug(f'Loading Bloomberg data from: {data_file}') return pd.DataFrame(pd.read_pickle(data_file)) process.init_request(request=request, tickers=tickers, flds=flds, **kwargs) logger.debug(f'Sending request to Bloomberg ...\n{request}') conn.send_request(request=request, **kwargs) res = pd.DataFrame( process.rec_events(func=process.process_ref, **kwargs)) if kwargs.get('raw', False): return res if res.empty or any(fld not in res for fld in ['ticker', 'field']): return pd.DataFrame() data = (res.set_index(['ticker', 'field']).droplevel( axis=0, level=1).rename_axis(index=None).pipe( pipeline.standard_cols, col_maps=kwargs.get('col_maps', None))) if data_file: logger.debug(f'Saving Bloomberg data to: {data_file}') files.create_folder(data_file, is_file=True) data.to_pickle(data_file) return data return pd.DataFrame( pd.concat( [bds(tickers=ticker, flds=flds, **kwargs) for ticker in tickers], sort=False))
def connect_bbg(**kwargs) -> blpapi.session.Session: """ Create Bloomberg session and make connection """ logger = logs.get_logger(connect_bbg, **kwargs) sess_opts = blpapi.SessionOptions() sess_opts.setServerHost('localhost') sess_opts.setServerPort(kwargs.get('port', _PORT_)) session = blpapi.Session(sess_opts) logger.debug('Connecting to Bloomberg ...') if session.start(): return session else: raise ConnectionError('Cannot connect to Bloomberg')
def ccy_pair(local, base='USD') -> CurrencyPair: """ Currency pair info Args: local: local currency base: base currency Returns: CurrencyPair Examples: >>> ccy_pair(local='HKD', base='USD') CurrencyPair(ticker='HKD Curncy', factor=1.0, power=1) >>> ccy_pair(local='GBp') CurrencyPair(ticker='GBP Curncy', factor=100, power=-1) >>> ccy_pair(local='USD', base='GBp') CurrencyPair(ticker='GBP Curncy', factor=0.01, power=1) >>> ccy_pair(local='XYZ', base='USD') CurrencyPair(ticker='', factor=1.0, power=1) >>> ccy_pair(local='GBP', base='GBp') CurrencyPair(ticker='', factor=0.01, power=1) >>> ccy_pair(local='GBp', base='GBP') CurrencyPair(ticker='', factor=100.0, power=1) """ ccy_param = param.load_info(cat='ccy') if f'{local}{base}' in ccy_param: info = ccy_param[f'{local}{base}'] elif f'{base}{local}' in ccy_param: info = ccy_param[f'{base}{local}'] info['factor'] = 1. / info.get('factor', 1.) info['power'] = -info.get('power', 1) elif base.lower() == local.lower(): info = dict(ticker='') info['factor'] = 1. if base[-1].lower() == base[-1]: info['factor'] /= 100. if local[-1].lower() == local[-1]: info['factor'] *= 100. else: logger = logs.get_logger(ccy_pair) logger.error(f'incorrect currency - local {local} / base {base}') return CurrencyPair(ticker='', factor=1., power=1) if 'factor' not in info: info['factor'] = 1. if 'power' not in info: info['power'] = 1 return CurrencyPair(**info)
def save_intraday(data: pd.DataFrame, ticker: str, dt, typ='TRADE', **kwargs): """ Check whether data is done for the day and save Args: data: data ticker: ticker dt: date typ: [TRADE, BID, ASK, BID_BEST, ASK_BEST, BEST_BID, BEST_ASK] Examples: >>> os.environ['BBG_ROOT'] = 'xbbg/tests/data' >>> sample = pd.read_parquet('xbbg/tests/data/aapl.parq') >>> save_intraday(sample, 'AAPL US Equity', '2018-11-02') >>> # Invalid exchange >>> save_intraday(sample, 'AAPL XX Equity', '2018-11-02') >>> # Invalid empty data >>> save_intraday(pd.DataFrame(), 'AAPL US Equity', '2018-11-02') >>> # Invalid date - too close >>> cur_dt = utils.cur_time() >>> save_intraday(sample, 'AAPL US Equity', cur_dt) """ cur_dt = pd.Timestamp(dt).strftime('%Y-%m-%d') logger = logs.get_logger(save_intraday, level='debug') info = f'{ticker} / {cur_dt} / {typ}' data_file = bar_file(ticker=ticker, dt=dt, typ=typ) if not data_file: return if data.empty: logger.warning(f'data is empty for {info} ...') return exch = const.exch_info(ticker=ticker, **kwargs) if exch.empty: return end_time = pd.Timestamp( const.market_timing(ticker=ticker, dt=dt, timing='FINISHED', **kwargs)).tz_localize(exch.tz) now = pd.Timestamp('now', tz=exch.tz) - pd.Timedelta('1H') if end_time > now: logger.debug( f'skip saving cause market close ({end_time}) < now - 1H ({now}) ...' ) return logger.info(f'saving data to {data_file} ...') files.create_folder(data_file, is_file=True) data.to_parquet(data_file)
def market_timing(ticker, dt, timing='EOD', tz='local') -> str: """ Market close time for ticker Args: ticker: ticker name dt: date timing: [EOD (default), BOD] tz: conversion to timezone Returns: str: date & time Examples: >>> market_timing('7267 JT Equity', dt='2018-09-10') '2018-09-10 14:58' >>> market_timing('7267 JT Equity', dt='2018-09-10', tz=timezone.TimeZone.NY) '2018-09-10 01:58:00-04:00' >>> market_timing('7267 JT Equity', dt='2018-01-10', tz='NY') '2018-01-10 00:58:00-05:00' >>> market_timing('7267 JT Equity', dt='2018-09-10', tz='SPX Index') '2018-09-10 01:58:00-04:00' >>> market_timing('8035 JT Equity', dt='2018-09-10', timing='BOD') '2018-09-10 09:01' >>> market_timing('Z 1 Index', dt='2018-09-10', timing='FINISHED') '2018-09-10 21:00' >>> market_timing('TESTTICKER Corp', dt='2018-09-10') '' """ logger = logs.get_logger(market_timing) exch = pd.Series(exch_info(ticker=ticker)) if any(req not in exch.index for req in ['tz', 'allday', 'day']): logger.error(f'required exchange info cannot be found in {ticker} ...') return '' mkt_time = { 'BOD': exch.day[0], 'FINISHED': exch.allday[-1] }.get(timing, exch.day[-1]) cur_dt = pd.Timestamp(str(dt)).strftime('%Y-%m-%d') if tz == 'local': return f'{cur_dt} {mkt_time}' return timezone.tz_convert(f'{cur_dt} {mkt_time}', to_tz=tz, from_tz=exch.tz)
def bds(tickers, flds, use_port=False, **kwargs) -> pd.DataFrame: """ Bloomberg block data Args: tickers: ticker(s) flds: field use_port: use `PortfolioDataRequest` **kwargs: other overrides for query Returns: pd.DataFrame: block data """ logger = logs.get_logger(bds, **kwargs) part = partial(_bds_, fld=flds, logger=logger, use_port=use_port, **kwargs) if isinstance(tickers, str): tickers = [tickers] return pd.DataFrame(pd.concat(map(part, tickers), sort=False))
def bdp(tickers, flds, cache=False, **kwargs): """ Get reference data and save to Args: tickers: tickers flds: fields to query cache: bool - use cache to store data **kwargs: overrides Returns: pd.DataFrame Examples: >>> bdp('IQ US Equity', 'Crncy', raw=True) ticker field value 0 IQ US Equity Crncy USD >>> bdp('IQ US Equity', 'Crncy').reset_index() ticker crncy 0 IQ US Equity USD """ logger = logs.get_logger(bdp, level=kwargs.pop('log', logs.LOG_LEVEL)) con, _ = create_connection() ovrds = assist.proc_ovrds(**kwargs) logger.info( f'loading reference data from Bloomberg:\n' f'{assist.info_qry(tickers=tickers, flds=flds)}' ) data = con.ref(tickers=tickers, flds=flds, ovrds=ovrds) if not cache: return [data] qry_data = [] for r, snap in data.iterrows(): subset = [r] data_file = storage.ref_file( ticker=snap.ticker, fld=snap.field, ext='pkl', cache=cache, **kwargs ) if data_file: if not files.exists(data_file): qry_data.append(data.iloc[subset]) files.create_folder(data_file, is_file=True) data.iloc[subset].to_pickle(data_file) return qry_data
def bdp_bds_cache(func, tickers, flds, **kwargs) -> ToQuery: """ Find cached `BDP` / `BDS` queries Args: func: function name - bdp or bds tickers: tickers flds: fields **kwargs: other kwargs Returns: ToQuery(ticker, flds, kwargs) """ cache_data = [] log_level = kwargs.get('log', logs.LOG_LEVEL) logger = logs.get_logger(bdp_bds_cache, level=log_level) has_date = kwargs.pop('has_date', func == 'bds') cache = kwargs.get('cache', True) tickers = utils.flatten(tickers) flds = utils.flatten(flds) loaded = pd.DataFrame(data=0, index=tickers, columns=flds) for ticker, fld in product(tickers, flds): data_file = storage.ref_file( ticker=ticker, fld=fld, has_date=has_date, cache=cache, ext='pkl', **{k: v for k, v in kwargs.items() if k not in EXC_COLS}) if not files.exists(data_file): continue logger.debug(f'reading from {data_file} ...') cache_data.append(pd.read_pickle(data_file)) loaded.loc[ticker, fld] = 1 to_qry = loaded.where(loaded == 0)\ .dropna(how='all', axis=1).dropna(how='all', axis=0) return ToQuery(tickers=to_qry.index.tolist(), flds=to_qry.columns.tolist(), cached_data=cache_data)
def send_request(request: blpapi.request.Request, **kwargs): """ Send request to Bloomberg session Args: request: Bloomberg request """ logger = logs.get_logger(send_request, **kwargs) try: bbg_session(**kwargs).sendRequest(request=request) except blpapi.InvalidStateException as e: logger.exception(e) # Delete existing connection and send again port = kwargs.get('port', _PORT_) con_sym = f'{_CON_SYM_}//{port}' if con_sym in globals(): del globals()[con_sym] # No error handler for 2nd trial bbg_session(**kwargs).sendRequest(request=request)
def bdp(tickers, flds, **kwargs) -> pd.DataFrame: """ Bloomberg reference data Args: tickers: tickers flds: fields to query **kwargs: Bloomberg overrides Returns: pd.DataFrame """ logger = logs.get_logger(bdp, **kwargs) if isinstance(tickers, str): tickers = [tickers] if isinstance(flds, str): flds = [flds] request = process.create_request( service='//blp/refdata', request='ReferenceDataRequest', **kwargs, ) process.init_request(request=request, tickers=tickers, flds=flds, **kwargs) logger.debug(f'Sending request to Bloomberg ...\n{request}') conn.send_request(request=request, **kwargs) res = pd.DataFrame(process.rec_events(func=process.process_ref, **kwargs)) if kwargs.get('raw', False): return res if res.empty or any(fld not in res for fld in ['ticker', 'field']): return pd.DataFrame() return (res.set_index(['ticker', 'field']).unstack(level=1).rename_axis( index=None, columns=[None, None]).droplevel(axis=1, level=0).loc[:, res.field.unique()].pipe( pipeline.standard_cols, col_maps=kwargs.get('col_maps', None)))
def latest_file(path_name, keyword='', ext='', **kwargs) -> str: """ Latest modified file in folder Args: path_name: full path name keyword: keyword to search ext: file extension Returns: str: latest file name """ files = sort_by_modified( all_files(path_name=path_name, keyword=keyword, ext=ext, full_path=True) ) if not files: from xbbg.io import logs logger = logs.get_logger(latest_file, level=kwargs.pop('log', 'warning')) logger.debug(f'no file in folder: {path_name}') return '' return str(files[0]).replace('\\', '/')
def fut_ticker(gen_ticker: str, dt, freq: str, **kwargs) -> str: """ Get proper ticker from generic ticker Args: gen_ticker: generic ticker dt: date freq: futures contract frequency Returns: str: exact futures ticker """ logger = logs.get_logger(fut_ticker, **kwargs) dt = pd.Timestamp(dt) t_info = gen_ticker.split() pre_dt = pd.bdate_range(end='today', periods=1)[-1] same_month = (pre_dt.month == dt.month) and (pre_dt.year == dt.year) asset = t_info[-1] if asset in ['Index', 'Curncy', 'Comdty']: ticker = ' '.join(t_info[:-1]) prefix, idx, postfix = ticker[:-1], int(ticker[-1]) - 1, asset elif asset == 'Equity': ticker = t_info[0] prefix, idx, postfix = ticker[:-1], int(ticker[-1]) - 1, ' '.join( t_info[1:]) else: logger.error(f'unkonwn asset type for ticker: {gen_ticker}') return '' month_ext = 4 if asset == 'Comdty' else 2 months = pd.date_range(start=dt, periods=max(idx + month_ext, 3), freq=freq) logger.debug(f'pulling expiry dates for months: {months}') def to_fut(month): return prefix + const.Futures[month.strftime('%b')] + \ month.strftime('%y')[-1 if same_month else -2:] + ' ' + postfix fut = [to_fut(m) for m in months] logger.debug(f'trying futures: {fut}') # noinspection PyBroadException try: fut_matu = bdp(tickers=fut, flds='last_tradeable_dt') except Exception as e1: logger.error( f'error downloading futures contracts (1st trial) {e1}:\n{fut}') # noinspection PyBroadException try: fut = fut[:-1] logger.debug(f'trying futures (2nd trial): {fut}') fut_matu = bdp(tickers=fut, flds='last_tradeable_dt') except Exception as e2: logger.error( f'error downloading futures contracts (2nd trial) {e2}:\n{fut}' ) return '' if 'last_tradeable_dt' not in fut_matu: logger.warning(f'no futures found for {fut}') return '' fut_matu.sort_values(by='last_tradeable_dt', ascending=True, inplace=True) sub_fut = fut_matu[pd.DatetimeIndex(fut_matu.last_tradeable_dt) > dt] logger.debug(f'futures full chain:\n{fut_matu.to_string()}') logger.debug(f'getting index {idx} from:\n{sub_fut.to_string()}') return sub_fut.index.values[idx]
async def live(tickers, flds=None, info=None, max_cnt=0, options=None, **kwargs): """ Subscribe and getting data feeds from Args: tickers: list of tickers flds: fields to subscribe info: list of keys of interests (ticker will be included) max_cnt: max number of data points to receive Yields: dict: Bloomberg market data Examples: >>> # async for _ in live('SPY US Equity', info=const.LIVE_INFO): pass """ from collections.abc import Iterable logger = logs.get_logger(live, **kwargs) evt_typs = conn.event_types() if flds is None: s_flds = ['LAST_PRICE', 'BID', 'ASK'] else: if isinstance(flds, str): flds = [flds] s_flds = [fld.upper() for fld in flds] if isinstance(info, str): info = [info] if isinstance(info, Iterable): info = [key.upper() for key in info] if info is None: info = const.LIVE_INFO sess = conn.bbg_session(**kwargs) while sess.tryNextEvent(): pass with subscribe(tickers=tickers, flds=s_flds, options=options, **kwargs): cnt = 0 while True and cnt <= max_cnt: try: ev = sess.tryNextEvent() if ev is None: continue if evt_typs[ev.eventType()] != 'SUBSCRIPTION_DATA': continue for msg, fld in product(ev, s_flds): if not msg.hasElement(fld): continue if msg.getElement(fld).isNull(): continue yield { **{ 'TICKER': msg.correlationIds()[0].value(), 'FIELD': fld, }, **{ str(elem.name()): process.elem_value(elem) for elem in msg.asElement().elements() if (True if not info else str(elem.name( )) in info) }, } if max_cnt: cnt += 1 except ValueError as e: logger.debug(e) except KeyboardInterrupt: break
def bdtick(ticker, dt, session='allday', time_range=None, types=None, **kwargs) -> pd.DataFrame: """ Bloomberg tick data Args: ticker: ticker name dt: date to download session: [allday, day, am, pm, pre, post] time_range: tuple of start and end time (must be converted into UTC) if this is given, `dt` and `session` will be ignored types: str or list, one or combinations of [ TRADE, AT_TRADE, BID, ASK, MID_PRICE, BID_BEST, ASK_BEST, BEST_BID, BEST_ASK, ] Returns: pd.DataFrame """ logger = logs.get_logger(bdtick, **kwargs) if types is None: types = ['TRADE'] exch = const.exch_info(ticker=ticker, **kwargs) if exch.empty: raise LookupError(f'Cannot find exchange info for {ticker}') if isinstance(time_range, (tuple, list)) and (len(time_range) == 2): cur_dt = pd.Timestamp(dt).strftime('%Y-%m-%d') time_rng = (pd.DatetimeIndex([ f'{cur_dt} {time_range[0]}', f'{cur_dt} {time_range[1]}', ]).tz_localize(exch.tz).tz_convert( process.DEFAULT_TZ).tz_convert('UTC')) else: time_rng = process.time_range(dt=dt, ticker=ticker, session=session, **kwargs) while conn.bbg_session(**kwargs).tryNextEvent(): pass request = process.create_request( service='//blp/refdata', request='IntradayTickRequest', settings=[ ('security', ticker), ('startDateTime', time_rng[0]), ('endDateTime', time_rng[1]), ('includeConditionCodes', True), ('includeExchangeCodes', True), ('includeNonPlottableEvents', True), ('includeBrokerCodes', True), ('includeRpsCodes', True), ('includeTradeTime', True), ('includeActionCodes', True), ('includeIndicatorCodes', True), ], append={'eventTypes': types}, **kwargs, ) logger.debug(f'Sending request to Bloomberg ...\n{request}') conn.send_request(request=request) res = pd.DataFrame( process.rec_events(func=process.process_bar, typ='t', **kwargs)) if kwargs.get('raw', False): return res if res.empty or ('time' not in res): return pd.DataFrame() return (res.set_index('time').rename_axis( index=None).tz_localize('UTC').tz_convert(exch.tz).pipe( pipeline.add_ticker, ticker=ticker).rename( columns={ 'size': 'volume', 'type': 'typ', 'conditionCodes': 'cond', 'exchangeCode': 'exch', 'tradeTime': 'trd_time', }))
def bdib(ticker: str, dt, session='allday', typ='TRADE', **kwargs) -> pd.DataFrame: """ Bloomberg intraday bar data Args: ticker: ticker name dt: date to download session: [allday, day, am, pm, pre, post] typ: [TRADE, BID, ASK, BID_BEST, ASK_BEST, BEST_BID, BEST_ASK] **kwargs: ref: reference ticker or exchange used as supplement if exchange info is not defined for `ticker` batch: whether is batch process to download data log: level of logs Returns: pd.DataFrame """ from xbbg.core import trials logger = logs.get_logger(bdib, **kwargs) ex_info = const.exch_info(ticker=ticker, **kwargs) if ex_info.empty: raise KeyError(f'Cannot find exchange info for {ticker}') ss_rng = process.time_range(dt=dt, ticker=ticker, session=session, tz=ex_info.tz, **kwargs) data_file = storage.bar_file(ticker=ticker, dt=dt, typ=typ) if files.exists(data_file) and kwargs.get( 'cache', True) and (not kwargs.get('reload', False)): res = (pd.read_parquet(data_file).pipe( pipeline.add_ticker, ticker=ticker).loc[ss_rng[0]:ss_rng[1]]) if not res.empty: logger.debug(f'Loading Bloomberg intraday data from: {data_file}') return res if not process.check_current(dt=dt, logger=logger, **kwargs): return pd.DataFrame() cur_dt = pd.Timestamp(dt).strftime('%Y-%m-%d') q_tckr = ticker if ex_info.get('is_fut', False): is_sprd = ex_info.get( 'has_sprd', False) and (len(ticker[:-1]) != ex_info['tickers'][0]) if not is_sprd: q_tckr = fut_ticker(gen_ticker=ticker, dt=dt, freq=ex_info['freq']) if q_tckr == '': logger.error(f'cannot find futures ticker for {ticker} ...') return pd.DataFrame() info_log = f'{q_tckr} / {cur_dt} / {typ}' trial_kw = dict(ticker=ticker, dt=dt, typ=typ, func='bdib') num_trials = trials.num_trials(**trial_kw) if num_trials >= 2: if kwargs.get('batch', False): return pd.DataFrame() logger.info(f'{num_trials} trials with no data {info_log}') return pd.DataFrame() while conn.bbg_session(**kwargs).tryNextEvent(): pass time_rng = process.time_range(dt=dt, ticker=ticker, session='allday', **kwargs) request = process.create_request( service='//blp/refdata', request='IntradayBarRequest', settings=[ ('security', ticker), ('eventType', typ), ('interval', kwargs.get('interval', 1)), ('startDateTime', time_rng[0]), ('endDateTime', time_rng[1]), ], **kwargs, ) logger.debug(f'Sending request to Bloomberg ...\n{request}') conn.send_request(request=request, **kwargs) res = pd.DataFrame(process.rec_events(func=process.process_bar, **kwargs)) if res.empty or ('time' not in res): logger.warning(f'No data for {info_log} ...') trials.update_trials(cnt=num_trials + 1, **trial_kw) return pd.DataFrame() data = (res.set_index('time').rename_axis(index=None).rename( columns={ 'numEvents': 'num_trds' }).tz_localize('UTC').tz_convert(ex_info.tz).pipe(pipeline.add_ticker, ticker=ticker)) if kwargs.get('cache', True): storage.save_intraday(data=data[ticker], ticker=ticker, dt=dt, typ=typ, **kwargs) return data.loc[ss_rng[0]:ss_rng[1]]
def bdtick(ticker, dt, session='allday', types=None, **kwargs) -> pd.DataFrame: """ Bloomberg tick data Args: ticker: ticker name dt: date to download session: [allday, day, am, pm, pre, post] types: str or list, one or combinations of [ TRADE, AT_TRADE, BID, ASK, MID_PRICE, BID_BEST, ASK_BEST, BEST_BID, BEST_ASK, ] Returns: pd.DataFrame """ logger = logs.get_logger(bdtick, **kwargs) exch = const.exch_info(ticker=ticker, **kwargs) time_rng = process.time_range(dt=dt, ticker=ticker, session=session, tz=exch.tz, **kwargs) service = conn.bbg_service(service='//blp/refdata', **kwargs) request = service.createRequest('IntradayTickRequest') while conn.bbg_session(**kwargs).tryNextEvent(): pass if types is None: types = ['TRADE'] if isinstance(types, str): types = [types] request.set('security', ticker) for typ in types: request.append('eventTypes', typ) request.set('startDateTime', time_rng[0]) request.set('endDateTime', time_rng[1]) request.set('includeConditionCodes', True) request.set('includeExchangeCodes', True) request.set('includeNonPlottableEvents', True) request.set('includeBrokerCodes', True) request.set('includeRpsCodes', True) request.set('includeTradeTime', True) request.set('includeActionCodes', True) request.set('includeIndicatorCodes', True) logger.debug(f'Sending request to Bloomberg ...\n{request}') conn.send_request(request=request) res = pd.DataFrame( process.rec_events(func=process.process_bar, typ='t', **kwargs)) if kwargs.get('raw', False): return res if res.empty or ('time' not in res): return pd.DataFrame() return (res.set_index('time').rename_axis( index=None).tz_localize('UTC').tz_convert(exch.tz).pipe( pipeline.add_ticker, ticker=ticker).rename( columns={ 'size': 'volume', 'type': 'typ', 'conditionCodes': 'cond', 'exchangeCode': 'exch', 'tradeTime': 'trd_time', }))
def bds(tickers, flds, cache=False, **kwargs): """ Download block data from Bloomberg Args: tickers: ticker(s) flds: field(s) cache: whether read from cache **kwargs: other overrides for query -> raw: raw output from `pdbdp` library, default False Returns: pd.DataFrame: block data Examples: >>> import os >>> >>> pd.options.display.width = 120 >>> s_dt, e_dt = '20180301', '20181031' >>> dvd = bds( ... 'NVDA US Equity', 'DVD_Hist_All', ... DVD_Start_Dt=s_dt, DVD_End_Dt=e_dt, raw=True, ... ) >>> dvd.loc[:, ['ticker', 'name', 'value']].head(8) ticker name value 0 NVDA US Equity Declared Date 2018-08-16 1 NVDA US Equity Ex-Date 2018-08-29 2 NVDA US Equity Record Date 2018-08-30 3 NVDA US Equity Payable Date 2018-09-21 4 NVDA US Equity Dividend Amount 0.15 5 NVDA US Equity Dividend Frequency Quarter 6 NVDA US Equity Dividend Type Regular Cash 7 NVDA US Equity Declared Date 2018-05-10 >>> dvd = bds( ... 'NVDA US Equity', 'DVD_Hist_All', ... DVD_Start_Dt=s_dt, DVD_End_Dt=e_dt, ... ) >>> dvd.reset_index().loc[:, ['ticker', 'ex_date', 'dividend_amount']] ticker ex_date dividend_amount 0 NVDA US Equity 2018-08-29 0.15 1 NVDA US Equity 2018-05-23 0.15 >>> if not os.environ.get('BBG_ROOT', ''): ... os.environ['BBG_ROOT'] = f'{files.abspath(__file__, 1)}/tests/data' >>> idx_kw = dict(End_Dt='20181220', cache=True) >>> idx_wt = bds('DJI Index', 'Indx_MWeight_Hist', **idx_kw) >>> idx_wt.round(2).tail().reset_index(drop=True) index_member percent_weight 0 V UN 3.82 1 VZ UN 1.63 2 WBA UW 2.06 3 WMT UN 2.59 4 XOM UN 2.04 >>> idx_wt = bds('DJI Index', 'Indx_MWeight_Hist', **idx_kw) >>> idx_wt.round(2).head().reset_index(drop=True) index_member percent_weight 0 AAPL UW 4.65 1 AXP UN 2.84 2 BA UN 9.29 3 CAT UN 3.61 4 CSCO UW 1.26 """ logger = logs.get_logger(bds, level=kwargs.pop('log', logs.LOG_LEVEL)) has_date = kwargs.pop('has_date', True) con, _ = create_connection() ovrds = assist.proc_ovrds(**kwargs) logger.info( f'loading block data from Bloomberg:\n' f'{assist.info_qry(tickers=tickers, flds=flds)}' ) data = con.bulkref(tickers=tickers, flds=flds, ovrds=ovrds) if not cache: return [data] qry_data = [] for (ticker, fld), grp in data.groupby(['ticker', 'field']): data_file = storage.ref_file( ticker=ticker, fld=fld, has_date=has_date, ext='pkl', cache=cache, **kwargs ) if data_file: if not files.exists(data_file): qry_data.append(grp) files.create_folder(data_file, is_file=True) grp.reset_index(drop=True).to_pickle(data_file) return qry_data
def live(tickers, flds='Last_Price', max_cnt=None, json=False, **kwargs) -> dict: """ Subscribe and getting data feeds from Args: tickers: list of tickers flds: fields to subscribe max_cnt: max number of data points to receive json: if data is required to convert to json Yields: dict: Bloomberg market data """ logger = logs.get_logger(live, **kwargs) def get_value(element): """ Get value from element Args: element: Bloomberg element Returns: dict """ conv = [conn.blpapi.name.Name] if json: conv += [pd.Timestamp, datetime.time, datetime.date] if element.isNull(): return None value = element.getValue() if isinstance(value, np.bool_): return bool(value) if isinstance(value, tuple(conv)): return str(value) return value if isinstance(flds, str): flds = [flds] s_flds = [fld.upper() for fld in flds] with subscribe(tickers=tickers, flds=s_flds, **kwargs): cnt = 0 while True if max_cnt is None else cnt < max_cnt: try: ev = conn.bbg_session(**kwargs).nextEvent(500) if conn.event_types()[ev.eventType()] != 'SUBSCRIPTION_DATA': continue for msg in ev: for fld in s_flds: if not msg.hasElement(fld): continue if msg.getElement(fld).isNull(): continue ticker = msg.correlationIds()[0].value() values = { **{ 'TICKER': ticker }, **{ str(elem.name()): get_value(elem) for elem in msg.asElement().elements() } } yield { key: value for key, value in values.items() if value not in [np.nan, pd.NaT, None] or ( isinstance(value, str) and value.strip()) } cnt += 1 except ValueError as e: logger.debug(e) except KeyboardInterrupt: break
def bdib(ticker, dt, typ='TRADE', batch=False, log=logs.LOG_LEVEL) -> pd.DataFrame: """ Download intraday data and save to cache Args: ticker: ticker name dt: date to download typ: [TRADE, BID, ASK, BID_BEST, ASK_BEST, BEST_BID, BEST_ASK] batch: whether is batch process to download data log: level of logs Returns: pd.DataFrame """ from xbbg.core import missing logger = logs.get_logger(bdib, level=log) t_1 = pd.Timestamp('today').date() - pd.Timedelta('1D') whole_day = pd.Timestamp(dt).date() < t_1 if (not whole_day) and batch: logger.warning(f'querying date {t_1} is too close, ignoring download ...') return pd.DataFrame() cur_dt = pd.Timestamp(dt).strftime('%Y-%m-%d') asset = ticker.split()[-1] info_log = f'{ticker} / {cur_dt} / {typ}' if asset in ['Equity', 'Curncy', 'Index', 'Comdty']: exch = const.exch_info(ticker=ticker) if exch.empty: return pd.DataFrame() else: logger.error(f'unknown asset type: {asset}') return pd.DataFrame() time_fmt = '%Y-%m-%dT%H:%M:%S' time_idx = pd.DatetimeIndex([ f'{cur_dt} {exch.allday[0]}', f'{cur_dt} {exch.allday[-1]}'] ).tz_localize(exch.tz).tz_convert(DEFAULT_TZ).tz_convert('UTC') if time_idx[0] > time_idx[1]: time_idx -= pd.TimedeltaIndex(['1D', '0D']) q_tckr = ticker if exch.get('is_fut', False): if 'freq' not in exch: logger.error(f'[freq] missing in info for {info_log} ...') is_sprd = exch.get('has_sprd', False) and (len(ticker[:-1]) != exch['tickers'][0]) if not is_sprd: q_tckr = fut_ticker(gen_ticker=ticker, dt=dt, freq=exch['freq']) if q_tckr == '': logger.error(f'cannot find futures ticker for {ticker} ...') return pd.DataFrame() info_log = f'{q_tckr} / {cur_dt} / {typ}' miss_kw = dict(ticker=ticker, dt=dt, typ=typ, func='bdib') cur_miss = missing.current_missing(**miss_kw) if cur_miss >= 2: if batch: return pd.DataFrame() logger.info(f'{cur_miss} trials with no data {info_log}') return pd.DataFrame() logger.info(f'loading data from Bloomberg: {info_log} ...') con, _ = create_connection() data = con.bdib( ticker=q_tckr, event_type=typ, interval=1, start_datetime=time_idx[0].strftime(time_fmt), end_datetime=time_idx[1].strftime(time_fmt), ) if not isinstance(data, pd.DataFrame): raise ValueError(f'unknown output format: {type(data)}') if data.empty: logger.warning(f'no data for {info_log} ...') missing.update_missing(**miss_kw) return pd.DataFrame() data = data.tz_localize('UTC').tz_convert(exch.tz) storage.save_intraday(data=data, ticker=ticker, dt=dt, typ=typ) return pd.DataFrame() if batch else assist.format_intraday(data=data, ticker=ticker)
def bdh(tickers, flds, start_date, end_date='today', adjust=None, **kwargs): """ Bloomberg historical data Args: tickers: ticker(s) flds: field(s) start_date: start date end_date: end date - default today adjust: `all`, `dvd`, `normal`, `abn` (=abnormal), `split`, `-` or None exact match of above words will adjust for corresponding events Case 0: `-` no adjustment for dividend or split Case 1: `dvd` or `normal|abn` will adjust for all dividends except splits Case 2: `adjust` will adjust for splits and ignore all dividends Case 3: `all` == `dvd|split` == adjust for all Case 4: None == Bloomberg default OR use kwargs **kwargs: overrides Returns: pd.DataFrame Examples: >>> bdh( ... tickers='VIX Index', flds=['High', 'Low', 'Last_Price'], ... start_date='2018-02-05', end_date='2018-02-07', ... ).round(2).transpose().rename_axis((None, None)) 2018-02-05 2018-02-06 2018-02-07 VIX Index High 38.80 50.30 31.64 Low 16.80 22.42 21.17 Last_Price 37.32 29.98 27.73 >>> bdh( ... tickers='AAPL US Equity', flds='Px_Last', ... start_date='20140605', end_date='20140610', adjust='-' ... ).round(2) ticker AAPL US Equity field Px_Last 2014-06-05 647.35 2014-06-06 645.57 2014-06-09 93.70 2014-06-10 94.25 >>> bdh( ... tickers='AAPL US Equity', flds='Px_Last', ... start_date='20140606', end_date='20140609', ... CshAdjNormal=False, CshAdjAbnormal=False, CapChg=False, ... ).round(2) ticker AAPL US Equity field Px_Last 2014-06-06 645.57 2014-06-09 93.70 """ logger = logs.get_logger(bdh, level=kwargs.pop('log', logs.LOG_LEVEL)) # Dividend adjustments if isinstance(adjust, str) and adjust: if adjust == 'all': kwargs['CshAdjNormal'] = True kwargs['CshAdjAbnormal'] = True kwargs['CapChg'] = True else: kwargs['CshAdjNormal'] = 'normal' in adjust or 'dvd' in adjust kwargs['CshAdjAbnormal'] = 'abn' in adjust or 'dvd' in adjust kwargs['CapChg'] = 'split' in adjust con, _ = create_connection() elms = assist.proc_elms(**kwargs) ovrds = assist.proc_ovrds(**kwargs) if isinstance(tickers, str): tickers = [tickers] if isinstance(flds, str): flds = [flds] s_dt = utils.fmt_dt(start_date, fmt='%Y%m%d') e_dt = utils.fmt_dt(end_date, fmt='%Y%m%d') logger.info( f'loading historical data from Bloomberg:\n' f'{assist.info_qry(tickers=tickers, flds=flds)}' ) return con.bdh( tickers=tickers, flds=flds, elms=elms, ovrds=ovrds, start_date=s_dt, end_date=e_dt, ).rename_axis(None)
def wrapper(*args, **kwargs): scope = utils.func_scope(func=func) param = inspect.signature(func).parameters port = kwargs.pop('port', _PORT_) timeout = kwargs.pop('timeout', _TIMEOUT_) restart = kwargs.pop('restart', False) all_kw = { k: args[n] if n < len(args) else v.default for n, (k, v) in enumerate(param.items()) if k != 'kwargs' } all_kw.update(kwargs) log_level = kwargs.get('log', logs.LOG_LEVEL) for to_list in ['tickers', 'flds']: conv = all_kw.get(to_list, None) if hasattr(conv, 'tolist'): all_kw[to_list] = getattr(conv, 'tolist')() if isinstance(conv, str): all_kw[to_list] = [conv] cached_data = [] if scope in ['xbbg.blp.bdp', 'xbbg.blp.bds']: to_qry = cached.bdp_bds_cache(func=func.__name__, **all_kw) cached_data += to_qry.cached_data if not (to_qry.tickers and to_qry.flds): if not cached_data: return pd.DataFrame() res = pd.concat(cached_data, sort=False).reset_index(drop=True) if not all_kw.get('raw', False): res = assist.format_output(data=res, source=func.__name__, col_maps=all_kw.get( 'col_maps', dict())) return res all_kw['tickers'] = to_qry.tickers all_kw['flds'] = to_qry.flds if scope in ['xbbg.blp.bdib']: data_file = storage.hist_file( ticker=all_kw['ticker'], dt=all_kw['dt'], typ=all_kw['typ'], ) if files.exists(data_file): logger = logs.get_logger(func, level=log_level) if all_kw.get('batch', False): return logger.debug(f'reading from {data_file} ...') return assist.format_intraday(data=pd.read_parquet(data_file), **all_kw) _, new = create_connection(port=port, timeout=timeout, restart=restart) res = func( ** {k: v for k, v in all_kw.items() if k not in ['raw', 'col_maps']}) if new: delete_connection() if scope.startswith('xbbg.blp.') and isinstance(res, list): final = cached_data + res if not final: return pd.DataFrame() res = pd.DataFrame(pd.concat(final, sort=False)) if (scope in ['xbbg.blp.bdp', 'xbbg.blp.bds']) \ and (not all_kw.get('raw', False)): res = assist.format_output( data=res.reset_index(drop=True), source=func.__name__, col_maps=all_kw.get('col_maps', dict()), ) return res