中文

Stock Screener

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 help you 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 specific parameter meanings.

Parameters

ParameterTypeRequiredDescription
markettigeropen.common.consts.MarketYesUS for US stocks, SG for Singapore stocks, HK for Hong Kong stocks
filterslist[StockFilter]YesList of filters, four categories in total, see explanation below
sort_field_datatigeropen.quote.domain.filter.SortFilterDataNoSort field object with main attributes as shown below
∟ fieldenumNoSort field, field enum from tigeropen.common.consts.filter_fields, such as StockField, AccumulateField
∟ sort_dirtigeropen.common.consts.SortDirectionNoSort direction, including: no sort, ascending, descending
pageintNoCurrent page number (starting from 0). Not recommended, please use cursor_id
cursor_idstrNoCursor ID for cursor-based pagination queries. Clients should pass this value when getting the next page, can pass None for the first query
page_sizeintNoNumber of data items returned per page, maximum supported configuration: 200

StockFilter parameter explanation:

ParameterTypeRequiredDescription
fieldtigeropen.common.consts.filter_fields.FilterFieldYesFour types of fields, see explanation below
filter_minfloatNoLower limit of range (closed interval), not passing means lower limit is -∞. For percentiles, don't add %, e.g., for 10%, just use value 10
filter_maxfloatNoUpper limit of range (closed interval), not passing means upper limit is +∞
is_no_filterboolNoWhether to disable this filter. If True, this filter will not take effect
accumulate_periodtigeropen.common.consts.filter_fields.AccumulatePeriodNoAccumulation period enum, only needed when field is AccumulateField
financial_periodtigeropen.common.consts.filter_fields.FinancialPeriodNoFinancial period enum, only needed when field is FinancialField
tag_listlist[int,str]NoTag list, only needed when field is MultiTagField

StockFilter field has the following enum types (import path: tigeropen.common.consts.filter_fields)

TypeDescription
StockFieldSimple indicator filter conditions, including price (OHLC, latest price, etc.), volume, share capital, market value, price change, P/E ratio, turnover rate, and other factors. Filter field meaning explanation: Filter Field Description
AccumulateFieldCumulative indicator filter conditions, including cumulative price change, asset growth rate, net profit growth rate, earnings per share, net profit, operating profit, operating income, ROA (return on equity), operating cash flow, debt-to-asset ratio, etc. Cumulative indicator periods can be: last 5 minutes, last 5 days, 10 days, 20 days, last half year, one year, two years, five years, Q1 report, Q3 report, interim report, etc. Filter field meaning explanation: Filter Field Description
FinancialFieldFinancial indicator filter conditions, including gross profit, net profit margin, total debt/shareholders' equity, total debt/total assets, current ratio, return on assets, net profit, operating cash flow, total assets, Hong Kong Stock Connect net purchases, annualized return, etc. Financial indicators currently only support LTM (Last Twelve Months annual report indicators) type financial report queries. Filter field meaning explanation: Filter Field Description
MultiTagFieldMulti-label association filter conditions, based on industry, concept, historical stock price high (current day stock price compared to historical prices), 52-week stock price high (current day stock price compared to last 52 weeks), whether OTC, whether options are supported, stock type (stock, ETF), whether broken IPO, etc. Filter field meaning explanation: Filter Field Description

The currency of price fields in filter parameters is consistent with the currency type of the market where the underlying asset is located, such as US stocks: USD, Hong Kong stocks: HKD, Singapore stocks: SGD, etc.

Returns

tigeropen.quote.domain.filter.ScannerResult

Structure 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 Filter stocks with dividend yield greater than 5% and revenue 3-year compound annual growth rate 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_alternative
from 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

ParameterTypeRequiredDescription
markettigeropen.common.consts.MarketYesUS for US stocks, SG for Singapore stocks, HK for Hong Kong stocks
tag_fieldslist[tigeropen.common.consts.filter_fields.MultiTagField]YesSupported field enum values: MultiTagField.Industry, MultiTagField.Concept

Returns

list. Each item as follows:

FieldTypeDescription
marketstrMarket code (US: US stocks, CN: A-shares, HK: Hong Kong stocks)
multi_tag_fieldstrMulti-tag field
tag_listlist[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"
            }
        ]
    }
]