o
    ä!«jM  ã                   @   sÌ   d Z ddlZddlmZ ddlmZ edƒZg d¢Zg d¢Zg d¢Z	e 
d	¡Zd
edefdd„Zd
edefdd„Zd
edefdd„Zd
edefdd„Zded
edeeef fdd„Zddedefdd„ZdS )ux  
symbol_filters.py

Shared filtering rules used by BOTH scanners (premarket_scanner.py's
universe pull, and top_stocks.py's replacement search) so a symbol
that's excluded in the morning can't sneak back in as a same-day
replacement candidate.

Three filter categories, all configurable via config.json -> universe:

1. ETFs / funds / trusts / notes  (exclude_etfs_and_funds)
2. Leveraged / inverse ("multiplier") products, e.g. 2x/3x bull/bear,
   ultra, daily-target products  (exclude_leveraged_inverse)
3. Company names that are too syllable-heavy to be a clean, liquid,
   easily-recognized single-purpose equity ticker  (max_name_syllables)

Alpaca's Asset objects (asset_class=US_EQUITY) include ETFs and ETNs â€”
US_EQUITY alone does NOT separate common stock from funds. Filtering
must be done on the asset's `name` field since there's no clean
is_etf boolean on the SDK object.
é    N)Ú
get_config)Ú
get_loggerÚsymbol_filters)ÚetfÚetnÚtrustz fundz
index fundzindex trustÚ	prosharesÚdirexionÚisharesÚspdrÚvaneckÚinvescozglobal xÚ
wisdomtreezark ÚvanguardÚgranitesharesz	simplify z	defiance Ú	roundhillÚtuttleÚyieldmaxzexchange tradedzunit investment trustzdepositary sharesz	notes duezexchange-traded)Ú2xÚ3xz-1xÚultraÚ
ultrashortzultra shortzbull zbear Ú	leveragedÚinversezdaily targetz
daily bullz
daily bearz	3x sharesz	2x sharesÚdoubleÚtriple)z
\binc\.?\bz\bincorporated\bz\bcorp\.?\bz\bcorporation\bz	\bco\.?\bz\bcompany\bz
\bltd\.?\bz\blimited\bz\bholdings?\bz	\bgroup\bz\bplc\bz\bllc\bz\bl\.p\.?\bz\bclass a\bz\bclass b\bz\bclass c\bz\bordinary shares?\bz\bcommon stock\bz\bsa\bz\bag\bz\bnv\bz	[aeiouy]+ÚnameÚreturnc                    ó&   | sdS |   ¡ ‰ t‡ fdd„tD ƒƒS )NFc                 3   ó   � | ]}|ˆ v V  qd S ©N© ©Ú.0Úkw©Únr!   ú)/var/www/screener/trade/symbol_filters.pyÚ	<genexpr><   ó   € z!is_etf_or_fund.<locals>.<genexpr>)ÚlowerÚanyÚ_FUND_KEYWORDS©r   r!   r%   r'   Úis_etf_or_fund8   ó   r.   c                    r   )NFc                 3   r   r    r!   r"   r%   r!   r'   r(   C   r)   z*is_leveraged_or_inverse.<locals>.<genexpr>)r*   r+   Ú_LEVERAGED_INVERSE_KEYWORDSr-   r!   r%   r'   Úis_leveraged_or_inverse?   r/   r1   c                 C   s$   |   ¡ }tD ]	}t |d|¡}q|S )NÚ )r*   Ú_LEGAL_SUFFIXESÚreÚsub)r   r&   Úpatternr!   r!   r'   Ú_strip_legal_suffixesF   s   r7   c                 C   sz   | sdS t | ƒ}t d|¡}d}|D ](}| ¡ }t |¡}t|ƒ}| d¡r3| d¡s3|dkr3|d8 }|t|dƒ7 }q|S )ua  
    Rough English syllable-count heuristic: strips legal suffixes
    (Inc, Corp, Class A, etc. shouldn't count against the company's
    "real" name), then counts vowel-groups per word, with a silent
    trailing-e correction. Good enough to flag genuinely long,
    multi-syllable company names â€” not intended to be linguistically
    perfect.
    r   z	[a-zA-Z]+ÚeÚleé   )r7   r4   Úfindallr*   Ú_VOWEL_GROUPSÚlenÚendswithÚmax)r   ÚcleanedÚwordsÚtotalÚwordÚwÚgroupsÚcountr!   r!   r'   Úcount_syllablesM   s   	
rG   Úsymbolc                 C   sr   t ƒ d }| dd¡rt|ƒrdS | dd¡rt|ƒrdS | d¡}|r7t|ƒ}||kr7dd	|› d
|› d�fS dS )zr
    Returns (passes, reason_if_rejected).
    Applies all three configured filters. Called by both scanners.
    ÚuniverseÚexclude_etfs_and_fundsT)FzETF/fund/trustÚexclude_leveraged_inverse)Fz&leveraged/inverse (multiplier) productÚmax_name_syllablesFzname too complex (z syllables > ú))TÚ )r   Úgetr.   r1   rG   )rH   r   ÚcfgÚmax_syllablesÚsylr!   r!   r'   Úpasses_symbol_filterse   s   

rS   TÚlog_rejectionsc                 C   sl   g }| D ]/}t |ddƒ}t |ddƒpd}|sqt||ƒ\}}|r&| |¡ q|r3t d|› d|› �¡ q|S )z‡
    assets: iterable of Alpaca Asset objects (must have .symbol and .name)
    Returns the list of symbols that pass all filters.
    rH   Nr   rN   z	[FILTER] z rejected: )ÚgetattrrS   ÚappendÚlogÚdebug)ÚassetsrT   Ú	survivorsÚarH   r   ÚokÚreasonr!   r!   r'   Úfilter_symbol_list{   s   €r^   )T)Ú__doc__r4   Úconfig_loaderr   Úlogger_setupr   rW   r,   r0   r3   Úcompiler<   ÚstrÚboolr.   r1   r7   ÚintrG   ÚtuplerS   Úlistr^   r!   r!   r!   r'   Ú<module>   s    	
