CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-webull

The unofficial python interface for the WeBull API

Overview
Eval results
Files

alerts-screening.mddocs/

Alerts & Screening

Price alert management and stock screening capabilities for market discovery and automated monitoring of price movements and market conditions.

Capabilities

Price Alerts Management

Create, manage, and remove price-based alerts for securities.

def alerts_list(self):
    """
    Get list of all active alerts.
    
    Returns:
    list: List of alert objects containing:
        - alertId: Unique alert identifier
        - tickerId: Security ticker ID
        - tickerSymbol: Stock symbol
        - alertType: Type of alert (price, smart, etc.)
        - triggerValue: Alert trigger value
        - isActive: Alert status
        - createTime: Alert creation timestamp
    
    Returns None if no alerts or request fails.
    """

def alerts_add(self, stock=None, frequency=1, interval=1, priceRules=[], smartRules=[]):
    """
    Add price or smart alerts for a security.
    
    Parameters:
    - stock (str, optional): Stock symbol to set alert for
    - frequency (int): Alert frequency setting
    - interval (int): Alert interval setting
    - priceRules (list): List of price rule objects containing:
        - type: Rule type ('PRICE_UP', 'PRICE_DOWN', 'PRICE_CROSS_UP', 'PRICE_CROSS_DOWN')
        - value: Trigger price or percentage
        - operator: Comparison operator ('>=', '<=', '>', '<')
    - smartRules (list): List of smart rule objects for advanced alerts
    
    Returns:
    dict: Alert creation result with alert ID
    """

def alerts_remove(self, alert=None, priceAlert=True, smartAlert=True):
    """
    Remove existing alerts.
    
    Parameters:
    - alert (dict, optional): Specific alert object to remove
    - priceAlert (bool): Remove price alerts
    - smartAlert (bool): Remove smart alerts
    
    Returns:
    dict: Removal result
    """

Usage examples:

# Get current alerts
alerts = wb.alerts_list()
if alerts:
    print(f"Active alerts: {len(alerts)}")
    for alert in alerts:
        print(f"Alert for {alert['tickerSymbol']}: {alert['alertType']}")
else:
    print("No active alerts")

# Add price alert for Apple above $160
price_rules = [{
    'type': 'PRICE_CROSS_UP',
    'value': 160.0,
    'operator': '>='
}]

alert_result = wb.alerts_add(
    stock='AAPL',
    frequency=1,
    interval=1,
    priceRules=price_rules
)

print(f"Price alert created: {alert_result}")

# Add percentage change alert
pct_rules = [{
    'type': 'PRICE_UP',
    'value': 5.0,  # 5% increase
    'operator': '>='
}]

wb.alerts_add(
    stock='TSLA',
    priceRules=pct_rules
)

# Remove specific alert
if alerts and len(alerts) > 0:
    wb.alerts_remove(alert=alerts[0])

# Remove all price alerts for cleanup
wb.alerts_remove(priceAlert=True, smartAlert=False)

Stock Screening

Discover stocks using built-in screening tools with customizable filters.

def run_screener(self, region=None, price_lte=None, price_gte=None, pct_chg_gte=None, pct_chg_lte=None, sort=None, volume_gte=None, market_cap_gte=None, market_cap_lte=None, pe_ratio_lte=None, dividend_yield_gte=None, ...):
    """
    Run stock screener with multiple filter criteria.
    
    Parameters:
    - region (str, optional): Market region ('US', 'HK', etc.)
    - price_lte (float, optional): Maximum stock price
    - price_gte (float, optional): Minimum stock price
    - pct_chg_gte (float, optional): Minimum percentage change
    - pct_chg_lte (float, optional): Maximum percentage change
    - sort (str, optional): Sort criteria ('price_asc', 'price_desc', 'pct_chg_desc', 'volume_desc')
    - volume_gte (int, optional): Minimum trading volume
    - market_cap_gte (float, optional): Minimum market capitalization
    - market_cap_lte (float, optional): Maximum market capitalization
    - pe_ratio_lte (float, optional): Maximum P/E ratio
    - dividend_yield_gte (float, optional): Minimum dividend yield
    - ... (additional screening parameters available)
    
    Returns:
    list: List of stocks matching criteria containing:
        - symbol: Stock symbol
        - name: Company name
        - price: Current price
        - change: Price change
        - changeRatio: Percentage change
        - volume: Trading volume
        - marketCap: Market capitalization
        - peRatio: Price-to-earnings ratio
        - dividendYield: Dividend yield
    """

Usage examples:

# Screen for stocks under $50 with >5% gain today
growth_stocks = wb.run_screener(
    region='US',
    price_lte=50.0,
    pct_chg_gte=5.0,
    volume_gte=1000000,  # Minimum 1M volume
    sort='pct_chg_desc'
)

print(f"Found {len(growth_stocks)} growth stocks:")
for stock in growth_stocks[:10]:  # Top 10
    print(f"{stock['symbol']}: ${stock['price']} ({stock['changeRatio']:+.2f}%)")

# Screen for dividend stocks
dividend_stocks = wb.run_screener(
    region='US',
    dividend_yield_gte=3.0,  # >3% dividend yield
    market_cap_gte=1000000000,  # >$1B market cap
    price_gte=10.0,  # >$10 price
    sort='dividend_yield_desc'
)

# Screen for value stocks
value_stocks = wb.run_screener(
    region='US',
    pe_ratio_lte=15.0,  # P/E ratio under 15
    market_cap_gte=500000000,  # >$500M market cap
    pct_chg_gte=-2.0,  # Not down more than 2%
    sort='pe_ratio_asc'
)

# Screen for high volume breakouts
breakout_stocks = wb.run_screener(
    region='US',
    pct_chg_gte=10.0,  # >10% gain
    volume_gte=5000000,  # >5M volume
    price_gte=5.0,  # >$5 price
    sort='volume_desc'
)

Market Discovery

Find active market movers and trending stocks.

def active_gainer_loser(self, direction='gainer', rank_type='afterMarket', count=50):
    """
    Get lists of active gainers, losers, or most active stocks.
    
    Parameters:
    - direction (str): List type - 'gainer', 'loser', or 'active'
    - rank_type (str): Market session - 'afterMarket', 'preMarket', 'regular'
    - count (int): Number of results to return (max 100)
    
    Returns:
    list: List of stocks with performance data containing:
        - symbol: Stock symbol
        - name: Company name
        - price: Current price
        - change: Price change
        - changeRatio: Percentage change
        - volume: Trading volume
        - marketCap: Market capitalization
    """

Usage examples:

# Get top gainers in regular trading
gainers = wb.active_gainer_loser(
    direction='gainer',
    rank_type='regular',
    count=20
)

print("Top Gainers:")
for stock in gainers:
    print(f"{stock['symbol']}: ${stock['price']} ({stock['changeRatio']:+.2f}%)")

# Get biggest losers
losers = wb.active_gainer_loser(
    direction='loser',
    rank_type='regular',
    count=15
)

print("\nBiggest Losers:")
for stock in losers:
    print(f"{stock['symbol']}: ${stock['price']} ({stock['changeRatio']:+.2f}%)")

# Get most active stocks by volume
most_active = wb.active_gainer_loser(
    direction='active',
    rank_type='regular',
    count=25
)

print("\nMost Active:")
for stock in most_active:
    print(f"{stock['symbol']}: Volume {stock['volume']:,}")

# Get pre-market movers
premarket_gainers = wb.active_gainer_loser(
    direction='gainer',
    rank_type='preMarket',
    count=10
)

# Get after-hours movers
afterhours_gainers = wb.active_gainer_loser(
    direction='gainer',
    rank_type='afterMarket',
    count=10
)

Advanced Screening Strategies

Multi-Criteria Stock Scanner

class StockScanner:
    def __init__(self, webull_client):
        self.wb = webull_client
    
    def momentum_scanner(self):
        """Scan for momentum stocks with strong price and volume action."""
        
        results = self.wb.run_screener(
            region='US',
            pct_chg_gte=5.0,  # >5% gain
            volume_gte=2000000,  # >2M volume
            price_gte=10.0,  # >$10 price
            market_cap_gte=100000000,  # >$100M market cap
            sort='pct_chg_desc'
        )
        
        print(f"Momentum Scanner Results ({len(results)} stocks):")
        for stock in results[:10]:
            print(f"{stock['symbol']}: ${stock['price']} ({stock['changeRatio']:+.2f}%) Vol: {stock['volume']:,}")
        
        return results
    
    def value_scanner(self):
        """Scan for undervalued stocks with good fundamentals."""
        
        results = self.wb.run_screener(
            region='US',
            pe_ratio_lte=20.0,  # P/E under 20
            market_cap_gte=1000000000,  # >$1B market cap
            price_gte=5.0,  # >$5 price
            dividend_yield_gte=1.0,  # >1% dividend
            pct_chg_gte=-5.0,  # Not down more than 5%
            sort='pe_ratio_asc'
        )
        
        print(f"Value Scanner Results ({len(results)} stocks):")
        for stock in results[:10]:
            pe = stock.get('peRatio', 'N/A')
            div_yield = stock.get('dividendYield', 'N/A')
            print(f"{stock['symbol']}: ${stock['price']} P/E: {pe} Div: {div_yield}%")
        
        return results
    
    def breakout_scanner(self):
        """Scan for stocks breaking out with high volume."""
        
        results = self.wb.run_screener(
            region='US',
            pct_chg_gte=8.0,  # >8% gain
            volume_gte=3000000,  # >3M volume
            price_gte=15.0,  # >$15 price
            sort='volume_desc'
        )
        
        # Filter for stocks near day high
        breakout_candidates = []
        for stock in results:
            try:
                # Get more detailed quote data
                quote = self.wb.get_quote(stock=stock['symbol'])
                high = float(quote.get('high', 0))
                current = float(quote.get('close', 0))
                
                # Check if current price is within 2% of day high
                if high > 0 and (current / high) >= 0.98:
                    breakout_candidates.append({
                        **stock,
                        'nearHigh': (current / high) * 100
                    })
            
            except Exception as e:
                continue
        
        print(f"Breakout Scanner Results ({len(breakout_candidates)} stocks):")
        for stock in breakout_candidates[:10]:
            print(f"{stock['symbol']}: ${stock['price']} ({stock['changeRatio']:+.2f}%) Near High: {stock['nearHigh']:.1f}%")
        
        return breakout_candidates
    
    def earnings_scanner(self):
        """Scan for stocks with upcoming earnings.""" 
        
        # Get stocks with recent high volume (potential earnings plays)
        results = self.wb.run_screener(
            region='US',
            volume_gte=5000000,  # >5M volume (unusual activity)
            price_gte=20.0,  # >$20 price
            market_cap_gte=1000000000,  # >$1B market cap
            sort='volume_desc'
        )
        
        print(f"Potential Earnings Plays ({len(results)} stocks):")
        for stock in results[:15]:
            print(f"{stock['symbol']}: Vol {stock['volume']:,} ({stock['changeRatio']:+.2f}%)")
        
        return results

# Usage
scanner = StockScanner(wb)

# Run different scans
momentum_stocks = scanner.momentum_scanner()
value_stocks = scanner.value_scanner()
breakout_stocks = scanner.breakout_scanner()
earnings_plays = scanner.earnings_scanner()

Alert Management System

class AlertManager:
    def __init__(self, webull_client):
        self.wb = webull_client
        self.active_alerts = {}
    
    def setup_watchlist_alerts(self, symbols, alert_type='percentage', threshold=5.0):
        """Set up alerts for a list of symbols."""
        
        for symbol in symbols:
            try:
                if alert_type == 'percentage':
                    # Set up percentage move alerts
                    up_rules = [{
                        'type': 'PRICE_UP',
                        'value': threshold,
                        'operator': '>='
                    }]
                    
                    down_rules = [{
                        'type': 'PRICE_DOWN',
                        'value': -threshold,
                        'operator': '<='
                    }]
                    
                    # Create upward move alert
                    up_alert = self.wb.alerts_add(
                        stock=symbol,
                        priceRules=up_rules
                    )
                    
                    # Create downward move alert
                    down_alert = self.wb.alerts_add(
                        stock=symbol,
                        priceRules=down_rules
                    )
                    
                    self.active_alerts[symbol] = {
                        'up_alert': up_alert,
                        'down_alert': down_alert,
                        'threshold': threshold
                    }
                    
                    print(f"Alerts set for {symbol}: ±{threshold}%")
                
                elif alert_type == 'price':
                    # Set price-based alerts
                    quote = self.wb.get_quote(stock=symbol)
                    current_price = float(quote['close'])
                    
                    upper_price = current_price * (1 + threshold/100)
                    lower_price = current_price * (1 - threshold/100)
                    
                    up_rules = [{
                        'type': 'PRICE_CROSS_UP',
                        'value': upper_price,
                        'operator': '>='
                    }]
                    
                    down_rules = [{
                        'type': 'PRICE_CROSS_DOWN',
                        'value': lower_price,
                        'operator': '<='
                    }]
                    
                    up_alert = self.wb.alerts_add(stock=symbol, priceRules=up_rules)
                    down_alert = self.wb.alerts_add(stock=symbol, priceRules=down_rules)
                    
                    self.active_alerts[symbol] = {
                        'up_alert': up_alert,
                        'down_alert': down_alert,
                        'upper_price': upper_price,
                        'lower_price': lower_price
                    }
                    
                    print(f"Price alerts set for {symbol}: ${lower_price:.2f} - ${upper_price:.2f}")
            
            except Exception as e:
                print(f"Failed to set alerts for {symbol}: {e}")
    
    def check_alert_status(self):
        """Check status of all active alerts."""
        
        all_alerts = self.wb.alerts_list()
        if not all_alerts:
            print("No active alerts")
            return
        
        print(f"Active Alerts ({len(all_alerts)}):")
        for alert in all_alerts:
            symbol = alert.get('tickerSymbol', 'Unknown')
            alert_type = alert.get('alertType', 'Unknown')
            is_active = alert.get('isActive', False)
            status = "🟢 Active" if is_active else "⚪ Inactive"
            
            print(f"  {symbol}: {alert_type} - {status}")
    
    def cleanup_alerts(self, symbols=None):
        """Remove alerts for specific symbols or all alerts."""
        
        if symbols:
            # Remove alerts for specific symbols
            all_alerts = self.wb.alerts_list()
            if all_alerts:
                for alert in all_alerts:
                    if alert.get('tickerSymbol') in symbols:
                        self.wb.alerts_remove(alert=alert)
                        print(f"Removed alert for {alert.get('tickerSymbol')}")
        else:
            # Remove all alerts
            self.wb.alerts_remove(priceAlert=True, smartAlert=True)
            print("All alerts removed")
        
        # Clear tracking
        if symbols:
            for symbol in symbols:
                self.active_alerts.pop(symbol, None)
        else:
            self.active_alerts.clear()

# Usage
alert_mgr = AlertManager(wb)

# Set up alerts for portfolio stocks
portfolio_symbols = ['AAPL', 'TSLA', 'MSFT', 'NVDA', 'AMD']
alert_mgr.setup_watchlist_alerts(portfolio_symbols, alert_type='percentage', threshold=3.0)

# Check alert status
alert_mgr.check_alert_status()

# Later, clean up alerts
# alert_mgr.cleanup_alerts(symbols=['AAPL', 'TSLA'])

Complete Screening & Alert Example

from webull import webull
import time

def market_discovery_system():
    """Complete market discovery and alert system."""
    
    wb = webull()
    wb.login('your_email@example.com', 'your_password')
    
    # Initialize scanner and alert manager
    scanner = StockScanner(wb)
    alert_mgr = AlertManager(wb)
    
    print("=== MARKET DISCOVERY SYSTEM ===")
    
    # 1. Run multiple scans
    print("\n1. Running Market Scans...")
    
    momentum_stocks = scanner.momentum_scanner()
    value_stocks = scanner.value_scanner()
    breakout_stocks = scanner.breakout_scanner()
    
    # 2. Combine results and find interesting opportunities
    print("\n2. Analyzing Opportunities...")
    
    all_candidates = []
    
    # Add momentum stocks with score
    for stock in momentum_stocks[:5]:
        all_candidates.append({
            **stock,
            'strategy': 'momentum',
            'score': stock['changeRatio']  # Use % change as score
        })
    
    # Add value stocks with score
    for stock in value_stocks[:5]:
        pe_ratio = stock.get('peRatio', 999)
        score = 100 / pe_ratio if pe_ratio > 0 else 0  # Inverse P/E as score
        all_candidates.append({
            **stock,
            'strategy': 'value',
            'score': score
        })
    
    # Add breakout stocks with score
    for stock in breakout_stocks[:3]:
        all_candidates.append({
            **stock,
            'strategy': 'breakout',
            'score': stock['changeRatio'] + (stock['volume'] / 1000000)  # Change + volume score
        })
    
    # 3. Rank all opportunities
    all_candidates.sort(key=lambda x: x['score'], reverse=True)
    
    print("\nTop Opportunities:")
    for i, stock in enumerate(all_candidates[:10]):
        print(f"{i+1}. {stock['symbol']} ({stock['strategy']}): Score {stock['score']:.2f}")
        print(f"   ${stock['price']} ({stock['changeRatio']:+.2f}%) Vol: {stock['volume']:,}")
    
    # 4. Set up alerts for top candidates
    print("\n3. Setting Up Alerts...")
    
    top_symbols = [stock['symbol'] for stock in all_candidates[:5]]
    alert_mgr.setup_watchlist_alerts(top_symbols, alert_type='percentage', threshold=2.0)
    
    # 5. Monitor for a period
    print("\n4. Monitoring Market Activity...")
    
    for minute in range(5):  # Monitor for 5 minutes
        print(f"\nMinute {minute + 1}:")
        
        # Check current gainers/losers
        current_gainers = wb.active_gainer_loser('gainer', 'regular', 5)
        current_losers = wb.active_gainer_loser('loser', 'regular', 5)
        
        print("Current Top Gainers:")
        for stock in current_gainers:
            print(f"  {stock['symbol']}: {stock['changeRatio']:+.2f}%")
        
        print("Current Top Losers:")
        for stock in current_losers:
            print(f"  {stock['symbol']}: {stock['changeRatio']:+.2f}%")
        
        time.sleep(60)  # Wait 1 minute
    
    # 6. Final report
    print("\n5. Final Alert Status:")
    alert_mgr.check_alert_status()
    
    # Cleanup
    print("\nCleaning up alerts...")
    alert_mgr.cleanup_alerts()

# Run the system
if __name__ == "__main__":
    market_discovery_system()

Best Practices

Alert Management

  • Use reasonable thresholds to avoid alert fatigue
  • Review and clean up alerts regularly
  • Combine price and volume criteria for better signals
  • Test alert triggers with paper trading first

Stock Screening

  • Use multiple criteria to filter results effectively
  • Combine fundamental and technical filters
  • Validate screener results with additional analysis
  • Save and backtest successful screening strategies

Risk Management

  • Set up alerts for portfolio positions to monitor risk
  • Use screening to find diversification opportunities
  • Monitor sector concentration through screening
  • Set up alerts for major market moves

Install with Tessl CLI

npx tessl i tessl/pypi-webull

docs

alerts-screening.md

authentication.md

index.md

market-data.md

options.md

paper-trading.md

portfolio.md

streaming.md

trading.md

tile.json