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.

Learning more: Example notebooks


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()

Learn more: setting up the environment


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:


Some of the built-in available filters include:


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:


sig_master_filter_brk = sig_master\
    .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:


sig_master_filter_brk_primary = sig_master_filter_brk\


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:


sig_master_filter_brk_xnys = sig_master_filter_brk\


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)

TRBC (The Refinitiv Business Classification) filtering #

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


all_classifications = {c: sig_master.show_filter_values(c).tolist()
                       for c in levels}

sig_master.filter_like(like='Industrials', column='TRBC_ECONOMIC_SECTOR')

Viewing SigMaster data#

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.

Integration with SingleStock objects #

SIGMaster supports the generation of SingleStock objects. This example filters SigMaster based on exchange tickers and operating MICs:


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_operating_mic(operating_mics=['XNAS', 'XNYS']) \


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:




{'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]}

Universe creation#

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)



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)

Filtering #

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.


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')



[[['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:






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.




['<', '<=', '==', '!=', '>=', '>', '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:


equity_filter.add('marketcap', '>', 1e8, 'daily')

equity_filter.add('volume', '>', 1e4, 'daily')

some_dates = pd.date_range(, 1, 15),, 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

Additional features #

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),

Multiple stock tradables may be issued by a listed company. The following code block returns the companies associated with the filtered stock IDs:


company_names = [sig.obj.get(id).company_name for id in ids]



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:


equity_filter = sig.EquityUniverseFilter(sm_universe)



Available frequencies

Given a fundamental or history field, the available_frequencies() method returns a list of frequencies available for all stocks in the universe:


available_frequencies = equity_filter.available_frequencies('Total Assets')



['Annual', 'Quarterly', 'Restated – Annual']