Single stocks
This page introduces Single Stock instruments and shows how you can define an equity universe through the EquityUniverseFilter or SigMaster classes.
Both classes are powerful tools for filtering an equity universe:
- The starting point for EquityUniverseFilter is defined as a stock index, which then can be filtered by a number of different metrics, such as fundamental data.
- The starting point for SigMaster is all securities available at a given point in time.
Set up your environment using the following three steps:
- Import the relevant internal and external libraries
- Configure the environment parameters
- Initialise the environment
import sigtech.framework as sig
import datetime as dtm
import pandas as pd
import numpy as np
import seaborn as sns
sns.set(rc={'figure.figsize': (18, 6)})
env = sig.config.init()
SigMaster is a point in time security master. It records all historic stock events, such as corporate actions and industry changes.
When such changes occur to a security, rather than updating values, SigMaster creates a new point in time copy of the security reflecting the change.
SigMaster also has a data model which stores information at the company, fungible and tradable levels:
- Company level: represents individual companies
- Fungible level: represents a particular share class for a given company
- Tradable level: represents all listings for a given fungible
SigMaster can be instantiated from the environment:
sig_master = env.sig_master()
sig_master.print_output = True # print any filtering to console
To view all the available filters that can be applied to the
sig_master
object and how they operate, run the following method:sig_master.show_filters()
Some of the built-in available filters include:
.filter_to_last_pit_record()
.filter_primary_tradable()
.filter_like()
.filter_operating_mic()
.filter_tradable()
.filter_tradable_active()
.filter_startswith()
.filter_primary_fungible()
.filter_primary()
.filter_notna()
These filters can be applied as multiple filters in sequence, also known as daisy chaining.
In the following example, SigMaster is filtered for all current Berkshire Hathaway securities:
Input
Output
sig_master_filter_brk = sig_master\
.filter_to_last_pit_record()\
.filter_like(like='BERKSHIRE HATHAWAY', column='ISSUE_NAME')
SIGMaster: filter_log_record=applied filter 'filter_to_last_pit_record' : SIG.TRADABLE_INTERNAL_ID ~duplicated keep='last' : 1674181 > 454466 records
SIGMaster: filter_log_record=applied filter 'filter_like' : ISSUE_NAME like BERKSHIRE HATHAWAY : 454466 > 53 records
The console logs show that the security master has been filtered to the 53 currently listed securities for Berkshire Hathaway.
This can be further filtered to retrieve the primary listings for the primary fungible or share class:
Input
Output
sig_master_filter_brk_primary = sig_master_filter_brk\
.filter_primary_fungible()\
.filter_primary_tradable()
SIGMaster: filter_log_record=applied filter 'filter_primary_fungible' : PRIMARY_FUNGIBLE == Y : 23 > 23 records
SIGMaster: filter_log_record=applied filter 'filter_primary_tradable' : PRIMARY_TRADABLE == Y : 23 > 2 records
This example picks out the 5 BRK securities listed on the NYSE, using the exchange's
operating_mic
:Input
Output
sig_master_filter_brk_xnys = sig_master_filter_brk\
.filter_operating_mic(operating_mics=['XNYS'])
SIGMaster: filter_log_record=applied filter 'filter_operating_mic' : OPERATING_MIC in ['XNYS'] : 23 > 5 records
The available filter values for any
sig_master
column can be retrieved using show_filter_values
:sig_master.show_filter_values(column='OPERATING_MIC', dropna=True)
Note: TRBC stands for The Refinitiv Business Classification
SigMaster is filtered according to the five-level TRBC classification scheme:
- Economic sectors
- Business sectors
- Industry groups
- Industries
- Activities
levels = ['TRBC_ECONOMIC_SECTOR', 'TRBC_BUSINESS_SECTOR',
'TRBC_INDUSTRY_GROUP', 'TRBC_INDUSTRY', 'TRBC_ACTIVITY' ]
all_classifications = {c: sig_master.show_filter_values(c).tolist()
for c in levels}
all_classifications
sig_master.filter_like(like='Industrials', column='TRBC_ECONOMIC_SECTOR')
There are a number of ways to transform a filtered
SIGMaster
object into a workable format. A few examples:.to_pandas_trading_view()
: provides fields relevant for trading and exchanges..to_pandas_data_model_view()
: useful for viewing the company/fungible/tradable data model..to_pandas_point_in_time_view()
: shows fields relevant for time travelling.
SIGMaster supports the generation of
SingleStock
objects. This example filters SigMaster based on exchange tickers and operating MICs:Input
Output
exchange_tickers = ['AAPL', 'BRKA', 'BRKB', 'MAT', 'ELAN',
'NVST', 'PG', 'PFE', 'WU', 'ESRT', 'BKI',
'JBGS', 'BHF', 'ROKU', 'SWCH', 'MDB']
sig_master_select_tickers = sig_master\
.filter_to_last_pit_record()\
.filter_primary_fungible()\
.filter_primary_tradable()\
.filter_operating_mic(operating_mics=['XNAS', 'XNYS']) \
.filter_exchange_tickers(exchange_tickers=exchange_tickers)
SIGMaster: filter_log_record=applied filter 'filter_to_last_corporate_event' : SIG.TRADABLE_INTERNAL_ID ~duplicated keep='last' : 2400974 > 471354 records
SIGMaster: filter_log_record=applied filter 'filter_primary_fungible' : PRIMARY_FUNGIBLE == Y : 471354 > 410832 records
SIGMaster: filter_log_record=applied filter 'filter_primary_tradable' : PRIMARY_TRADABLE == Y : 410832 > 112046 records
SIGMaster: filter_log_record=applied filter 'filter_operating_mic' : OPERATING_MIC in ['XNAS', 'XNYS'] : 112046 > 16346 records
SIGMaster: filter_log_record=applied filter 'filter_exchange_tickers' : EXCHANGE_TICKER in ['AAPL', 'BRKA', 'BRKB', 'MAT', 'ELAN', 'NVST', 'PG', 'PFE', 'WU', 'ESRT', 'BKI', 'JBGS', 'BHF', 'ROKU', 'SWCH', 'MDB'] : 16346 > 15 records
>>> as_of = 2021-05-02 00:00:00, 15 companies, 15 fungibles, 15 tradables, 2 operating mics, shape (15, 64), 6 filter(s) applied
Filter history:
- applied filter 'filter_as_of' : MARKET_TIMESTAMP <= 2021-05-02 00:00:00 : 2400974 > 2400974 records
- applied filter 'filter_to_last_corporate_event' : SIG.TRADABLE_INTERNAL_ID ~duplicated keep='last' : 2400974 > 471354 records
- applied filter 'filter_primary_fungible' : PRIMARY_FUNGIBLE == Y : 471354 > 410832 records
- applied filter 'filter_primary_tradable' : PRIMARY_TRADABLE == Y : 410832 > 112046 records
- applied filter 'filter_operating_mic' : OPERATING_MIC in ['XNAS', 'XNYS'] : 112046 > 16346 records
- applied filter 'filter_exchange_tickers' : EXCHANGE_TICKER in ['AAPL', 'BRKA', 'BRKB', 'MAT', 'ELAN', 'NVST', 'PG', 'PFE', 'WU', 'ESRT', 'BKI', 'JBGS', 'BHF', 'ROKU', 'SWCH', 'MDB'] : 16346 > 15 records
To access the resulting
SingleStock
objects:Input
Output
sig_master_select_tickers.to_single_stock()
{'MAT.XNAS': 1010987.SINGLE_STOCK.TRADABLE <class 'sigtech.framework.instruments.equities.SingleStock'>[139675400818448],
'WU.XNYS': 1036055.SINGLE_STOCK.TRADABLE <class 'sigtech.framework.instruments.equities.SingleStock'>[139675401378832],
'BKI.XNYS': 1123343.SINGLE_STOCK.TRADABLE <class 'sigtech.framework.instruments.equities.SingleStock'>[139675401361424],
'ROKU.XNAS': 1122878.SINGLE_STOCK.TRADABLE <class 'sigtech.framework.instruments.equities.SingleStock'>[139675401337232],
'SWCH.XNYS': 1123697.SINGLE_STOCK.TRADABLE <class 'sigtech.framework.instruments.equities.SingleStock'>[139675400463184],
'AAPL.XNAS': 1000045.SINGLE_STOCK.TRADABLE <class 'sigtech.framework.instruments.equities.SingleStock'>[139675400464976],
'MDB.XNAS': 1124358.SINGLE_STOCK.TRADABLE <class 'sigtech.framework.instruments.equities.SingleStock'>[139675400430096],
'NVST.XNYS': 1153683.SINGLE_STOCK.TRADABLE <class 'sigtech.framework.instruments.equities.SingleStock'>[139675400430352],
'ELAN.XNYS': 1145377.SINGLE_STOCK.TRADABLE <class 'sigtech.framework.instruments.equities.SingleStock'>[139675400430672],
'PFE.XNYS': 1011166.SINGLE_STOCK.TRADABLE <class 'sigtech.framework.instruments.equities.SingleStock'>[139675400430928],
'PG.XNYS': 1004551.SINGLE_STOCK.TRADABLE <class 'sigtech.framework.instruments.equities.SingleStock'>[139675400431120],
'BHF.XNAS': 1109859.SINGLE_STOCK.TRADABLE <class 'sigtech.framework.instruments.equities.SingleStock'>[139675400431376],
'ESRT.XNYS': 1078510.SINGLE_STOCK.TRADABLE <class 'sigtech.framework.instruments.equities.SingleStock'>[139675400431632],
'JBGS.XNYS': 1109694.SINGLE_STOCK.TRADABLE <class 'sigtech.framework.instruments.equities.SingleStock'>[139675400433296]}
The
get_universe_generator
method returns a sig_master
universe creation object:universe_generator = sig_master.get_universe_generator()
Filters can then be applied, similarly to
sig_master
:universe_generator = universe_generator.filter_operating_mic(operating_mics=['XNAS', 'XNYS'])
universe_generator = universe_generator.filter_exchange_tickers(exchange_tickers=exchange_tickers)
The
apply
method can be called with a list of dates to evaluate the universe. By default, the apply
method will filter each point in time for primary fungibles, primary tradable, and single stock securities. The resulting dictionary can be passed to the EquityUniverseFilter
for additional filtering:some_dates = [dtm.datetime(2021, 1, 4), dtm.datetime(2022, 1, 4)]
sm_universe = universe_generator.apply(some_dates, primary_fungible_only=True,
primary_tradable_only=True, single_stocks_only=True)
sm_universe
EquityUniverseFilter is a narrower, but faster way of defining a universe, starting with a stock index. It can also filter on data fields, such as:
- Fundamental
- Historic
- TRBC
An
EquityUniverseFilter
object takes a string representing an equity index identifier:equity_filter = sig.EquityUniverseFilter('SPX INDEX')
The
universe_ids()
method returns a list of index constituent IDs, which represent the universe at a point in time:constituents = equity_filter.universe_ids(env.asofdate)
The
add()
method takes a fundamental or history field, such as:- An operator
- A value/threshold
- The frequency of the field.
Fields, operators and frequencies are case-insensitive. To see the available fundamental and history fields we can use the
available_fields()
method on a stock object.Input
Output
equity_filter.add('market cap USD', '>', 1e4, 'Quarterly') # Denominated in millions.
equity_filter.add('Price/Earnings Ratio', '<', 50, 'annual')
equity_filter.add('Dividend yield', 'TOP', 5, 'quarterly')
equity_filter.add('Healthcare', 'in', 'TRBC_ECONOMIC_SECTOR')
equity_filter.list()
[[['Market Cap USD'], 10000.0, '>', ['Quarterly'], 'Absolute', None, None],
[['Price/Earnings Ratio'], 50, '<', ['Annual'], 'Absolute', None, None],
[['Dividend Yield'], 5, 'top', ['Quarterly'], 'Absolute', None, None],
[['Healthcare'], 'TRBC_ECONOMIC_SECTOR', 'in', None, 'Absolute', None, None]]
The
clean()
method removes all filters:Input
Output
equity_filter.clean()
equity_filter.list()
[]
The available filter operators available are listed below. When the 'Body, 5' operator is applied to a field it filters out the top 5 and bottom 5 stock IDs.
Input
Output
equity_filter.OPERATORS
['<', '<=', '==', '!=', '>=', '>', 'top', 'bottom', 'body']
Method
apply_filter()
takes a date or a list of dates and applies the filters defined using add()
in the following order:- 1.All comparison operators (
<
,<=
,==
,!=
,>=
,>
) are first applied in the order defined by the user. - 2.All ranking operators (
top
,bottom
,body
) are subsequently applied in the order defined by the user.
Parameter
ignore_asofdate
controls how the underlying time series is loaded by the filter engine:- If set to
True
, which is the default, the time series loaded will use the as of date set in the environment. - If set to
False
, the as of date of the time series coincides with the date in which the filter is applied. When applied to multiple dates, this setting may be more computationally expensive.
Only the time series for the fields specified in the filters are loaded. In the following example, filters are applied to multiple points in time. This returns a list of filtered stock identifiers for each point in time:
Input
Output
equity_filter.add('marketcap', '>', 1e8, 'daily')
equity_filter.add('volume', '>', 1e4, 'daily')
some_dates = pd.date_range(dtm.date(2019, 1, 15), dtm.date(2020, 9, 15), freq='Q')
universe = equity_filter.apply_filter(some_dates)
2019-03-31 [1002621.SINGLE_STOCK.TRADABLE, 1007362.SINGLE...
2019-06-30 [1002621.SINGLE_STOCK.TRADABLE, 1031274.SINGLE...
2019-09-30 [1151348.SINGLE_STOCK.TRADABLE, 1002621.SINGLE...
2019-12-31 [1151348.SINGLE_STOCK.TRADABLE, 1002621.SINGLE...
2020-03-31 [1151348.SINGLE_STOCK.TRADABLE, 1007362.SINGLE...
2020-06-30 [1151348.SINGLE_STOCK.TRADABLE, 1007362.SINGLE...
dtype: objec
This section contains examples of additional features, useful for debugging purposes or to analyse the results of a stock universe filter.
Filtered output
Method
display_fields_data()
returns a DataFrame containing time series data defined by a date and a list of stock ids, fields and frequencies:# Select first 10 unique stocks that were eligible at any point in time in our universe
ids = universe.explode().unique()[:10]
# The corresponding data was not loaded during filtering, it will load now
display_fields = ['TOTAL ASSETS', 'Net Sales or Revenues', 'VWAP']
display_frequencies = ['Quarterly', 'Quarterly', 'Annual', 'Daily']
equity_filter.display_fields_data(date=pd.Timestamp(2019, 1, 15),
ids=ids,
fields=display_fields,
frequencies=display_frequencies)

Multiple stock tradables may be issued by a listed company. The following code block returns the companies associated with the filtered stock IDs:
Input
Output
company_names = [sig.obj.get(id).company_name for id in ids]
company_names
['RAYMOND JAMES FINL.INCO.',
'T ROWE PRICE GROUP INCO.',
'WELLCARE HLTH.PLANS INCO',
'MASTERCARD INCO.',
'CHARLES SCHWAB CORP.']
Generic constructor
The
EquityUniverseFilter
constructor also supports a dict, such as date or list of stock IDs, for stock universe customisations. You can populate a data structure representing a stock universe with dates and lists of stock identifiers available on those dates. In the following example, the
sm_universe
dictionary generated from SigMaster is passed:Input
Output
equity_filter = sig.EquityUniverseFilter(sm_universe)
['1000004.SINGLE_STOCK.TRADABLE',
'1000008.SINGLE_STOCK.TRADABLE',
'1000045.SINGLE_STOCK.TRADABLE',
'1000094.SINGLE_STOCK.TRADABLE',
'1000141.SINGLE_STOCK.TRADABLE',
'1000164.SINGLE_STOCK.TRADABLE',
'1000271.SINGLE_STOCK.TRADABLE',
'1000296.SINGLE_STOCK.TRADABLE',
'1000317.SINGLE_STOCK.TRADABLE',
'1000328.SINGLE_STOCK.TRADABLE',
'1000336.SINGLE_STOCK.TRADABLE',
'1000344.SINGLE_STOCK.TRADABLE',
'1000365.SINGLE_STOCK.TRADABLE',
'1000448.SINGLE_STOCK.TRADABLE',
'1000484.SINGLE_STOCK.TRADABLE',
'1000581.SINGLE_STOCK.TRADABLE',
'1000705.SINGLE_STOCK.TRADABLE',
'1000761.SINGLE_STOCK.TRADABLE',
'1000805.SINGLE_STOCK.TRADABLE',
'1000854.SINGLE_STOCK.TRADABLE']
Available frequencies
Given a fundamental or history field, the
available_frequencies()
method returns a list of frequencies available for all stocks in the universe:Input
Output
available_frequencies = equity_filter.available_frequencies('Total Assets')
available_frequencies
['Annual', 'Quarterly', 'Restated – Annual']
Last modified 1yr ago