Stock Screener
Multi‑tag field type (e.g., industry or concept).## market_scanner Stock Screener
QuoteClient.market_scanner(self, market: Optional[Union[Market, str]] = Market.US, filters: Optional[List[StockFilter]] = None, sort_field_data: Optional[SortFilterData] = None, page: Optional[int] = 0, page_size: Optional[int] = 100, cursor_id: Optional[str] = None)
Description
Scan the entire market using different technical indicator conditions to filter out a list of securities that meet specific investment needs.
Technical indicator conditions include the following categories: basic indicators, cumulative indicators, financial indicators, and multi-label indicators. Please refer to the explanations below for the details on specific parameters.
Parameters
Parameter | Type | Required | Description |
|---|---|---|---|
market | Yes | Market identifier.
Supported values include: | |
filters | list[StockFilter] | Yes | List of filters, for more details see StockFilter Parameters |
sort_field_data | tigeropen.quote.domain.filter.SortFilterData | No | Sort field object with the two following attributes |
∟ field | enum | No | Sort field, must be an enum value from tigeropen.common.consts.filter_fields, such as StockField or AccumulateField |
∟ sort_dir | No | Sort order. Supported values: none, ascending, descending | |
page | int | No | Current page number, starting from 0. Not recommended for use; please use cursor_id instead. |
cursor_id | str | No | Cursor ID for cursor-based pagination. Clients should pass this value when requesting the next page. For the first request, pass |
page_size | int | No | Number of data items returned per page. Maximum supported value: 200 |
StockFilter Parameters:
Parameter | Type | Required | Description |
|---|---|---|---|
field | tigeropen.common.consts.filter_fields.FilterField | Yes | This enum defines four types of fields; see detailed explanations below |
filter_min | float | No | Lower bound of the filter range (inclusive). If not provided, the lower bound defaults to -∞. For percentile-based filters, do not include the percent sign. Example: for 10%, use the value 10 |
filter_max | float | No | Upper bound of the filter range (inclusive). If not provided, the upper bound defaults to +∞ |
is_no_filter | bool | No | Indicates whether this filter is disabled. If true, the filter will not be applied. |
accumulate_period | No | Accumulation period enum. Only required when the field is AccumulateField | |
financial_period | No | Financial period enum. Only required when the field is FinancialField | |
tag_list |
| No | List of tag values to filter on. Only required when the field is MultiTagField. |
StockFilter field has the following enum types (import path: tigeropen.common.consts.filter_fields)
Type | Description |
|---|---|
StockField | Simple technical indicator filters, including price (OHLC, latest price, etc.), trading volume, share capital, market value, price change, P/E ratio, turnover rate, and other related factors. For detailed field definitions, see Filter Field Description. |
AccumulateField | Cumulative indicator filters, including cumulative price change, asset growth rate, net profit growth rate, earnings per share, net profit, operating profit, operating income, ROE (return on equity), operating cash flow, debt-to-asset ratio, and similar metrics. Supported accumulation periods include, but are not limited to: last 5 minutes, last 5 days, 10 days, 20 days, half year, one year, two years, five years, Q1 report, Q3 report, and interim report. For detailed field definitions, see Filter Field Description |
FinancialField | Financial statement–based indicator filters, including gross profit, net profit margin, total debt/shareholders’ equity, total debt, total assets, current ratio, return on assets, net profit, operating cash flow, and total assets. Financial indicators currently support TTM (Last Twelve Months) and annual report–based queries only. For detailed field definitions, see Filter Field Description |
MultiTagField | Multi-label association filter conditions, based on industry, concept, and historical price characteristics, such as:
For detailed field definitions, see Filter Field Description |
Note: The currency used for price-related filter parameters is determined by the market of the underlying asset. For example, U.S. stocks would use USD
Returns
tigeropen.quote.domain.filter.ScannerResult
Class structure is as follows:
class ScannerResult:
def __init__(self, page, page_size, total_page, total_count, items, cursor_id):
# Current page number, starting from 0
self.page = page
# Total number of pages
self.total_page = total_page
# Total number of data items
self.total_count = total_count
# Page size
self.page_size = page_size
# Next page cursor ID
self.cursor_id = cursor_id
# Result data list
self.items: ScannerResultItem = list()
# Summary of filtered stock symbol list
self.symbols = list()
# Each item in items is:
class ScannerResultItem:
def __init__(self, symbol, market, base_data_list=None, accumulate_data_list=None, financial_data_list=None,
multi_tag_data_list=None):
self.symbol = symbol
self.market = market
self.field_data = dict()
# You can use filter as key to get the corresponding filter field value, see example below
Example
import time
from datetime import datetime
from tigeropen.quote.quote_client import QuoteClient
from tigeropen.tiger_open_config import get_client_config
from tigeropen.common.consts import TradingSession, Market
from tigeropen.quote.domain.filter import OptionFilter, StockFilter, SortFilterData
from tigeropen.common.consts.filter_fields import StockField, AccumulateField, FinancialField, MultiTagField, \
FinancialPeriod, AccumulatePeriod
from tigeropen.tiger_open_config import TigerOpenClientConfig
client_config = TigerOpenClientConfig(props_path='/path/to/your/properties/file/')
quote_client = QuoteClient(client_config)
def test_market_scanner():
# Stock basic data filter (is_no_filter=True means this filter is not enabled)
base_filter1 = StockFilter(StockField.FloatShare, filter_min=1e7, filter_max=1e13, is_no_filter=True)
base_filter2 = StockFilter(StockField.MarketValue, filter_min=1e8, filter_max=1e14, is_no_filter=False)
# Filter by earnings date
base_filter3 = StockFilter(StockField.EarningDate, filter_min=int(datetime.strptime('2021-01-01', '%Y-%m-%d').timestamp() * 1000),
filter_max=int(datetime.strptime('2023-12-31', '%Y-%m-%d').timestamp() * 1000)
, is_no_filter=False)
# Periodic cumulative data filter
accumulate_filter = StockFilter(AccumulateField.ChangeRate, filter_min=0.01, filter_max=1, is_no_filter=False,
accumulate_period=AccumulatePeriod.Last_Year)
# Financial data filter
financial_filter = StockFilter(FinancialField.LYR_PE, filter_min=1, filter_max=100, is_no_filter=False,
financial_period=FinancialPeriod.LTM)
# Multi-tag data filter, need to get tag_list first. If there are values, pass them according to the situation. If no values, don't pass tag_list parameter; For boolean fields, tag_list is generally empty and no value needs to be passed
tags = quote_client.get_market_scanner_tags(market=Market.US, tag_fields=[MultiTagField.OptionsAvailable])
if not tags:
tag_list = []
else:
# Need to replace with the desired tag based on actual situation, e.g., when field is MultiTagField.Industry, tag_list = ['BK4209']
tag_list = ['BK4209']
multi_tag_filter = StockFilter(MultiTagField.isOTC, tag_list=tag_list)
# Sort field
sort_field_data = SortFilterData(StockField.FloatShare, sort_dir=SortDirection.ASC)
cursor_id = None
page_size = 50
# Whether it's the last page of data
is_last_page = False
# Symbol list after filtering
scanner_result_symbols = set()
while not is_last_page:
# Put the filters you want to use in the filters parameter
result = quote_client.market_scanner(market=Market.US,
filters=[base_filter1, base_filter2,
# base_filter3,
accumulate_filter,
financial_filter,
multi_tag_filter],
sort_field_data=sort_field_data,
cursor_id=cursor_id,
page_size=page_size)
print(result)
if result.total_page:
for item in result.items:
# item type is ScannerResultItem
symbol = item.symbol
market = item.market
# You can get the value of a certain filter field in dictionary form
base_filter1_value = item[base_filter1]
accumulate_filter_value = item[accumulate_filter]
print(
f'page:{result.page}, symbol:{symbol}, base_filter1 value:{base_filter1_value}, accumulate_filter value:{accumulate_filter_value}')
print(f'current page symbols:{result.symbols}')
scanner_result_symbols.update(result.symbols)
time.sleep(10)
# Handle pagination
if not result.cursor_id:
is_last_page = True
else:
cursor_id = result.cursor_id
print(f'scanned symbols:{scanner_result_symbols}')Return Example
ScannerResult({'page': 0, 'total_page': 208, 'total_count': 1040, 'page_size': 5,
'cursor_id': 'xxxxxx',
'items': [
ScannerResultItem({'symbol': 'DNP', 'market': 'US',
'field_data': {
<StockField.FloatShare: 13>: 0.0,
<StockField.MarketValue: 17>: 3855828898.39,
<AccumulateField.ChangeRate: 1>: 0.043925,
<FinancialField.LYR_PE: 45>: 7.359675,
<MultiTagField.isOTC: 3>: '0'}}),
ScannerResultItem({'symbol': 'FEN', 'market': 'US',
'field_data': {
<StockField.FloatShare: 13>: 0.0,
<StockField.MarketValue: 17>: 278571284.64,
<AccumulateField.ChangeRate: 1>: 0.063893,
<FinancialField.LYR_PE: 45>: 6.45728,
<MultiTagField.isOTC: 3>: '0'}}),
ScannerResultItem({'symbol': 'FDUS', 'market': 'US',
'field_data': {
<StockField.FloatShare: 13>: 0.0,
<StockField.MarketValue: 17>: 462844356.0,
<AccumulateField.ChangeRate: 1>: 0.079202,
<FinancialField.LYR_PE: 45>: 3.986464,
<MultiTagField.isOTC: 3>: '0'}}),
ScannerResultItem({'symbol': 'KYN', 'market': 'US',
'field_data': {
<StockField.FloatShare: 13>: 0.0,
<StockField.MarketValue: 17>: 1181621680.4,
<AccumulateField.ChangeRate: 1>: 0.122898,
<FinancialField.LYR_PE: 45>: 3.268946,
<MultiTagField.isOTC: 3>: '0'}}),
ScannerResultItem({'symbol': 'TYG', 'market': 'US',
'field_data': {
<StockField.FloatShare: 13>: 0.0,
<StockField.MarketValue: 17>: 381692896.0,
<AccumulateField.ChangeRate: 1>: 0.180812,
<FinancialField.LYR_PE: 45>: 2.853998,
<MultiTagField.isOTC: 3>: '0'}})],
'symbols': ['FEN', 'DNP', 'FDUS', 'KYN', 'TYG']})
Example 1 Select stocks with a dividend yield above 5% and a revenue compound annual growth rate over the past three years greater than 10%.
from tigeropen.quote.quote_client import QuoteClient
from tigeropen.common.consts import TradingSession
from tigeropen.quote.domain.filter import OptionFilter, StockFilter, SortFilterData
from tigeropen.common.consts.filter_fields import StockField, AccumulateField, FinancialField, MultiTagField, \
FinancialPeriod, AccumulatePeriod
from tigeropen.tiger_open_config import TigerOpenClientConfig
client_config = TigerOpenClientConfig(props_path='/path/to/your/properties/file/')
quote_client = QuoteClient(client_config)
def test_market_scanner1():
# Dividend yield greater than 5%
base_filter = StockFilter(StockField.DivideRate, filter_min=0.05)
# Total revenue 3-year growth rate or revenue 3-year compound annual growth rate
financial_filter = StockFilter(FinancialField.TotalRevenues3YrCagr, filter_min=0.1)
cursor_id = None
page_size = 50
# Whether it's the last page of data
is_last_page = False
# Symbol list after filtering
scanner_result_symbols = set()
while not is_last_page:
# Put the filters you want to use in the filters parameter
result = quote_client.market_scanner(market=Market.US,
filters=[
base_filter,
financial_filter,
],
cursor_id=cursor_id,
page_size=page_size)
print(result)
if result.total_page:
for item in result.items:
symbol = item.symbol
market = item.market
# You can get the value of a certain filter field in dictionary form
base_filter_value = item[base_filter]
financial_filter_value = item[financial_filter]
print(
f'page:{result.page}, symbol:{symbol}, base_filter value:{base_filter_value}, financial_filter value:{financial_filter_value}')
print(f'current page symbols:{result.symbols}')
scanner_result_symbols.update(result.symbols)
time.sleep(10)
# Handle pagination
if result.cursor_id is None:
is_last_page = True
else:
cursor_id = result.cursor_id
print(f'scanned symbols:{scanner_result_symbols}')Example 2 Filter by ETF type
Optional ETF type tag values in tag_list
Hot ETFs: package_us_v1_etf_hot
Bank ETF: package_us_v1_etf_bank
Bond ETF: package_us_v1_etf_bond
Buffer ETF: package_us_v1_etf_buffer
Broad Index ETF: package_us_v1_etf_index
Leveraged & Inverse ETF: package_us_v1_etf_leverage
Sector ETF: package_us_v1_etf_sector
Single Stock Leveraged ETF: package_us_v1_etf_single_stock
Market Cap ETF: package_us_v1_etf_market_cap
Thematic ETF: package_us_v1_etf_thematic
International ETF: package_us_v1_etf_international
Growth & Value ETF: package_us_v1_etf_growth
Commodity ETF: package_us_v1_etf_commodity
ARK ETF: package_us_v1_etf_ark
Volatility ETF: package_us_v1_etf_volatility
Currency ETF: package_us_v1_etf_currency
Alternative Investment ETF: package_us_v1_etf_alternativefrom tigeropen.quote.quote_client import QuoteClient
from tigeropen.tiger_open_config import get_client_config
from tigeropen.common.consts import Market
from tigeropen.quote.domain.filter import StockFilter
from tigeropen.common.consts.filter_fields import MultiTagField
client_config = get_client_config(private_key_path='private_key_path', tiger_id='your tiger id', account='your account')
quote_client = QuoteClient(client_config)
def test_market_scanner_etf():
# Filter by ETF type, here we filter growth and alternative investment ETFs
multi_tag_filter_etftype = StockFilter(MultiTagField.ETF_TYPE, tag_list=["package_us_v1_etf_growth", "package_us_v1_etf_alternative"])
cursor_id = None
page_size = 50
# Whether it's the last page of data
is_last_page = False
# Symbol list after filtering
scanner_result_symbols = set()
while not is_last_page:
result = quote_client.market_scanner(market=Market.US,
filters=[multi_tag_filter_etftype],
cursor_id=cursor_id,
page_size=page_size)
if result.total_page:
for item in result.items:
symbol = item.symbol
market = item.market
etf_type_value = item[multi_tag_filter_etftype]
print(f'page:{result.page}, symbol:{symbol}, etf_type:{etf_type_value}')
print(f'current page symbols:{result.symbols}')
scanner_result_symbols.update(result.symbols)
time.sleep(10)
# Handle pagination
if result.cursor_id is None:
is_last_page = True
else:
cursor_id = result.cursor_id
print(f'scanned symbols:{scanner_result_symbols}')get_market_scanner_tags
QuoteClient.get_market_scanner_tags(self, market=Market.US, tag_fields=None)
Description
Get tag values for multi-label association filter fields. Currently only supports getting industry and concept tag sets.
Parameters
Parameter | Type | Required | Description |
|---|---|---|---|
market | Yes | Market identifier.
Supported values include: | |
tag_fields |
| Yes | List of supported multi‑tag field enum values
Supported values include: |
Returns
list. Each item as follows:
| Field | Type | Description |
|---|---|---|
| market | str | Market code (e.g., US for U.S. stocks) |
| multi_tag_field | str | Multi‑tag field type (e.g., industry or concept) |
| tag_list | list[str] | Set of tags that can be used to filter multi-tag fields |
Example
from tigeropen.quote.quote_client import QuoteClient
from tigeropen.common.consts import TradingSession, Market
from tigeropen.common.consts.filter_fields import MultiTagField
from tigeropen.tiger_open_config import TigerOpenClientConfig
client_config = TigerOpenClientConfig(props_path='/path/to/your/properties/file/')
quote_client = QuoteClient(client_config)
field_list = [ MultiTagField.Concept, MultiTagField.Industry]
result = quote_client.get_market_scanner_tags(market=Market.US, fields=field_list)
print(result)Return Example
[
{
"market": "US",
"multi_tag_field": "MultiTagField_Concept",
"tag_list": [
{
"tag": "BK4565",
"value": "NFT Concept"
},
{
"tag": "BK4564",
"value": "Space Concept"
},
{
"tag": "BK4567",
"value": "ESG Concept"
},
{
"tag": "BK4566",
"value": "Capital Group"
},
{
"tag": "BK4568",
"value": "US Anti-Pandemic Concept"
},
{
"tag": "BK4561",
"value": "Soros Holdings"
},
{
"tag": "BK4560",
"value": "Cybersecurity Concept"
}
]
}
]Updated 7 days ago