The unofficial python interface for the WeBull API
Price alert management and stock screening capabilities for market discovery and automated monitoring of price movements and market conditions.
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)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'
)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
)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()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'])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()Install with Tessl CLI
npx tessl i tessl/pypi-webull