or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

alerts-screening.mdauthentication.mdindex.mdmarket-data.mdoptions.mdpaper-trading.mdportfolio.mdstreaming.mdtrading.md

alerts-screening.mddocs/

0

# Alerts & Screening

1

2

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

3

4

## Capabilities

5

6

### Price Alerts Management

7

8

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

9

10

```python { .api }

11

def alerts_list(self):

12

"""

13

Get list of all active alerts.

14

15

Returns:

16

list: List of alert objects containing:

17

- alertId: Unique alert identifier

18

- tickerId: Security ticker ID

19

- tickerSymbol: Stock symbol

20

- alertType: Type of alert (price, smart, etc.)

21

- triggerValue: Alert trigger value

22

- isActive: Alert status

23

- createTime: Alert creation timestamp

24

25

Returns None if no alerts or request fails.

26

"""

27

28

def alerts_add(self, stock=None, frequency=1, interval=1, priceRules=[], smartRules=[]):

29

"""

30

Add price or smart alerts for a security.

31

32

Parameters:

33

- stock (str, optional): Stock symbol to set alert for

34

- frequency (int): Alert frequency setting

35

- interval (int): Alert interval setting

36

- priceRules (list): List of price rule objects containing:

37

- type: Rule type ('PRICE_UP', 'PRICE_DOWN', 'PRICE_CROSS_UP', 'PRICE_CROSS_DOWN')

38

- value: Trigger price or percentage

39

- operator: Comparison operator ('>=', '<=', '>', '<')

40

- smartRules (list): List of smart rule objects for advanced alerts

41

42

Returns:

43

dict: Alert creation result with alert ID

44

"""

45

46

def alerts_remove(self, alert=None, priceAlert=True, smartAlert=True):

47

"""

48

Remove existing alerts.

49

50

Parameters:

51

- alert (dict, optional): Specific alert object to remove

52

- priceAlert (bool): Remove price alerts

53

- smartAlert (bool): Remove smart alerts

54

55

Returns:

56

dict: Removal result

57

"""

58

```

59

60

Usage examples:

61

62

```python

63

# Get current alerts

64

alerts = wb.alerts_list()

65

if alerts:

66

print(f"Active alerts: {len(alerts)}")

67

for alert in alerts:

68

print(f"Alert for {alert['tickerSymbol']}: {alert['alertType']}")

69

else:

70

print("No active alerts")

71

72

# Add price alert for Apple above $160

73

price_rules = [{

74

'type': 'PRICE_CROSS_UP',

75

'value': 160.0,

76

'operator': '>='

77

}]

78

79

alert_result = wb.alerts_add(

80

stock='AAPL',

81

frequency=1,

82

interval=1,

83

priceRules=price_rules

84

)

85

86

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

87

88

# Add percentage change alert

89

pct_rules = [{

90

'type': 'PRICE_UP',

91

'value': 5.0, # 5% increase

92

'operator': '>='

93

}]

94

95

wb.alerts_add(

96

stock='TSLA',

97

priceRules=pct_rules

98

)

99

100

# Remove specific alert

101

if alerts and len(alerts) > 0:

102

wb.alerts_remove(alert=alerts[0])

103

104

# Remove all price alerts for cleanup

105

wb.alerts_remove(priceAlert=True, smartAlert=False)

106

```

107

108

### Stock Screening

109

110

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

111

112

```python { .api }

113

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, ...):

114

"""

115

Run stock screener with multiple filter criteria.

116

117

Parameters:

118

- region (str, optional): Market region ('US', 'HK', etc.)

119

- price_lte (float, optional): Maximum stock price

120

- price_gte (float, optional): Minimum stock price

121

- pct_chg_gte (float, optional): Minimum percentage change

122

- pct_chg_lte (float, optional): Maximum percentage change

123

- sort (str, optional): Sort criteria ('price_asc', 'price_desc', 'pct_chg_desc', 'volume_desc')

124

- volume_gte (int, optional): Minimum trading volume

125

- market_cap_gte (float, optional): Minimum market capitalization

126

- market_cap_lte (float, optional): Maximum market capitalization

127

- pe_ratio_lte (float, optional): Maximum P/E ratio

128

- dividend_yield_gte (float, optional): Minimum dividend yield

129

- ... (additional screening parameters available)

130

131

Returns:

132

list: List of stocks matching criteria containing:

133

- symbol: Stock symbol

134

- name: Company name

135

- price: Current price

136

- change: Price change

137

- changeRatio: Percentage change

138

- volume: Trading volume

139

- marketCap: Market capitalization

140

- peRatio: Price-to-earnings ratio

141

- dividendYield: Dividend yield

142

"""

143

```

144

145

Usage examples:

146

147

```python

148

# Screen for stocks under $50 with >5% gain today

149

growth_stocks = wb.run_screener(

150

region='US',

151

price_lte=50.0,

152

pct_chg_gte=5.0,

153

volume_gte=1000000, # Minimum 1M volume

154

sort='pct_chg_desc'

155

)

156

157

print(f"Found {len(growth_stocks)} growth stocks:")

158

for stock in growth_stocks[:10]: # Top 10

159

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

160

161

# Screen for dividend stocks

162

dividend_stocks = wb.run_screener(

163

region='US',

164

dividend_yield_gte=3.0, # >3% dividend yield

165

market_cap_gte=1000000000, # >$1B market cap

166

price_gte=10.0, # >$10 price

167

sort='dividend_yield_desc'

168

)

169

170

# Screen for value stocks

171

value_stocks = wb.run_screener(

172

region='US',

173

pe_ratio_lte=15.0, # P/E ratio under 15

174

market_cap_gte=500000000, # >$500M market cap

175

pct_chg_gte=-2.0, # Not down more than 2%

176

sort='pe_ratio_asc'

177

)

178

179

# Screen for high volume breakouts

180

breakout_stocks = wb.run_screener(

181

region='US',

182

pct_chg_gte=10.0, # >10% gain

183

volume_gte=5000000, # >5M volume

184

price_gte=5.0, # >$5 price

185

sort='volume_desc'

186

)

187

```

188

189

### Market Discovery

190

191

Find active market movers and trending stocks.

192

193

```python { .api }

194

def active_gainer_loser(self, direction='gainer', rank_type='afterMarket', count=50):

195

"""

196

Get lists of active gainers, losers, or most active stocks.

197

198

Parameters:

199

- direction (str): List type - 'gainer', 'loser', or 'active'

200

- rank_type (str): Market session - 'afterMarket', 'preMarket', 'regular'

201

- count (int): Number of results to return (max 100)

202

203

Returns:

204

list: List of stocks with performance data containing:

205

- symbol: Stock symbol

206

- name: Company name

207

- price: Current price

208

- change: Price change

209

- changeRatio: Percentage change

210

- volume: Trading volume

211

- marketCap: Market capitalization

212

"""

213

```

214

215

Usage examples:

216

217

```python

218

# Get top gainers in regular trading

219

gainers = wb.active_gainer_loser(

220

direction='gainer',

221

rank_type='regular',

222

count=20

223

)

224

225

print("Top Gainers:")

226

for stock in gainers:

227

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

228

229

# Get biggest losers

230

losers = wb.active_gainer_loser(

231

direction='loser',

232

rank_type='regular',

233

count=15

234

)

235

236

print("\nBiggest Losers:")

237

for stock in losers:

238

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

239

240

# Get most active stocks by volume

241

most_active = wb.active_gainer_loser(

242

direction='active',

243

rank_type='regular',

244

count=25

245

)

246

247

print("\nMost Active:")

248

for stock in most_active:

249

print(f"{stock['symbol']}: Volume {stock['volume']:,}")

250

251

# Get pre-market movers

252

premarket_gainers = wb.active_gainer_loser(

253

direction='gainer',

254

rank_type='preMarket',

255

count=10

256

)

257

258

# Get after-hours movers

259

afterhours_gainers = wb.active_gainer_loser(

260

direction='gainer',

261

rank_type='afterMarket',

262

count=10

263

)

264

```

265

266

## Advanced Screening Strategies

267

268

### Multi-Criteria Stock Scanner

269

270

```python

271

class StockScanner:

272

def __init__(self, webull_client):

273

self.wb = webull_client

274

275

def momentum_scanner(self):

276

"""Scan for momentum stocks with strong price and volume action."""

277

278

results = self.wb.run_screener(

279

region='US',

280

pct_chg_gte=5.0, # >5% gain

281

volume_gte=2000000, # >2M volume

282

price_gte=10.0, # >$10 price

283

market_cap_gte=100000000, # >$100M market cap

284

sort='pct_chg_desc'

285

)

286

287

print(f"Momentum Scanner Results ({len(results)} stocks):")

288

for stock in results[:10]:

289

print(f"{stock['symbol']}: ${stock['price']} ({stock['changeRatio']:+.2f}%) Vol: {stock['volume']:,}")

290

291

return results

292

293

def value_scanner(self):

294

"""Scan for undervalued stocks with good fundamentals."""

295

296

results = self.wb.run_screener(

297

region='US',

298

pe_ratio_lte=20.0, # P/E under 20

299

market_cap_gte=1000000000, # >$1B market cap

300

price_gte=5.0, # >$5 price

301

dividend_yield_gte=1.0, # >1% dividend

302

pct_chg_gte=-5.0, # Not down more than 5%

303

sort='pe_ratio_asc'

304

)

305

306

print(f"Value Scanner Results ({len(results)} stocks):")

307

for stock in results[:10]:

308

pe = stock.get('peRatio', 'N/A')

309

div_yield = stock.get('dividendYield', 'N/A')

310

print(f"{stock['symbol']}: ${stock['price']} P/E: {pe} Div: {div_yield}%")

311

312

return results

313

314

def breakout_scanner(self):

315

"""Scan for stocks breaking out with high volume."""

316

317

results = self.wb.run_screener(

318

region='US',

319

pct_chg_gte=8.0, # >8% gain

320

volume_gte=3000000, # >3M volume

321

price_gte=15.0, # >$15 price

322

sort='volume_desc'

323

)

324

325

# Filter for stocks near day high

326

breakout_candidates = []

327

for stock in results:

328

try:

329

# Get more detailed quote data

330

quote = self.wb.get_quote(stock=stock['symbol'])

331

high = float(quote.get('high', 0))

332

current = float(quote.get('close', 0))

333

334

# Check if current price is within 2% of day high

335

if high > 0 and (current / high) >= 0.98:

336

breakout_candidates.append({

337

**stock,

338

'nearHigh': (current / high) * 100

339

})

340

341

except Exception as e:

342

continue

343

344

print(f"Breakout Scanner Results ({len(breakout_candidates)} stocks):")

345

for stock in breakout_candidates[:10]:

346

print(f"{stock['symbol']}: ${stock['price']} ({stock['changeRatio']:+.2f}%) Near High: {stock['nearHigh']:.1f}%")

347

348

return breakout_candidates

349

350

def earnings_scanner(self):

351

"""Scan for stocks with upcoming earnings."""

352

353

# Get stocks with recent high volume (potential earnings plays)

354

results = self.wb.run_screener(

355

region='US',

356

volume_gte=5000000, # >5M volume (unusual activity)

357

price_gte=20.0, # >$20 price

358

market_cap_gte=1000000000, # >$1B market cap

359

sort='volume_desc'

360

)

361

362

print(f"Potential Earnings Plays ({len(results)} stocks):")

363

for stock in results[:15]:

364

print(f"{stock['symbol']}: Vol {stock['volume']:,} ({stock['changeRatio']:+.2f}%)")

365

366

return results

367

368

# Usage

369

scanner = StockScanner(wb)

370

371

# Run different scans

372

momentum_stocks = scanner.momentum_scanner()

373

value_stocks = scanner.value_scanner()

374

breakout_stocks = scanner.breakout_scanner()

375

earnings_plays = scanner.earnings_scanner()

376

```

377

378

### Alert Management System

379

380

```python

381

class AlertManager:

382

def __init__(self, webull_client):

383

self.wb = webull_client

384

self.active_alerts = {}

385

386

def setup_watchlist_alerts(self, symbols, alert_type='percentage', threshold=5.0):

387

"""Set up alerts for a list of symbols."""

388

389

for symbol in symbols:

390

try:

391

if alert_type == 'percentage':

392

# Set up percentage move alerts

393

up_rules = [{

394

'type': 'PRICE_UP',

395

'value': threshold,

396

'operator': '>='

397

}]

398

399

down_rules = [{

400

'type': 'PRICE_DOWN',

401

'value': -threshold,

402

'operator': '<='

403

}]

404

405

# Create upward move alert

406

up_alert = self.wb.alerts_add(

407

stock=symbol,

408

priceRules=up_rules

409

)

410

411

# Create downward move alert

412

down_alert = self.wb.alerts_add(

413

stock=symbol,

414

priceRules=down_rules

415

)

416

417

self.active_alerts[symbol] = {

418

'up_alert': up_alert,

419

'down_alert': down_alert,

420

'threshold': threshold

421

}

422

423

print(f"Alerts set for {symbol}: ±{threshold}%")

424

425

elif alert_type == 'price':

426

# Set price-based alerts

427

quote = self.wb.get_quote(stock=symbol)

428

current_price = float(quote['close'])

429

430

upper_price = current_price * (1 + threshold/100)

431

lower_price = current_price * (1 - threshold/100)

432

433

up_rules = [{

434

'type': 'PRICE_CROSS_UP',

435

'value': upper_price,

436

'operator': '>='

437

}]

438

439

down_rules = [{

440

'type': 'PRICE_CROSS_DOWN',

441

'value': lower_price,

442

'operator': '<='

443

}]

444

445

up_alert = self.wb.alerts_add(stock=symbol, priceRules=up_rules)

446

down_alert = self.wb.alerts_add(stock=symbol, priceRules=down_rules)

447

448

self.active_alerts[symbol] = {

449

'up_alert': up_alert,

450

'down_alert': down_alert,

451

'upper_price': upper_price,

452

'lower_price': lower_price

453

}

454

455

print(f"Price alerts set for {symbol}: ${lower_price:.2f} - ${upper_price:.2f}")

456

457

except Exception as e:

458

print(f"Failed to set alerts for {symbol}: {e}")

459

460

def check_alert_status(self):

461

"""Check status of all active alerts."""

462

463

all_alerts = self.wb.alerts_list()

464

if not all_alerts:

465

print("No active alerts")

466

return

467

468

print(f"Active Alerts ({len(all_alerts)}):")

469

for alert in all_alerts:

470

symbol = alert.get('tickerSymbol', 'Unknown')

471

alert_type = alert.get('alertType', 'Unknown')

472

is_active = alert.get('isActive', False)

473

status = "🟢 Active" if is_active else "⚪ Inactive"

474

475

print(f" {symbol}: {alert_type} - {status}")

476

477

def cleanup_alerts(self, symbols=None):

478

"""Remove alerts for specific symbols or all alerts."""

479

480

if symbols:

481

# Remove alerts for specific symbols

482

all_alerts = self.wb.alerts_list()

483

if all_alerts:

484

for alert in all_alerts:

485

if alert.get('tickerSymbol') in symbols:

486

self.wb.alerts_remove(alert=alert)

487

print(f"Removed alert for {alert.get('tickerSymbol')}")

488

else:

489

# Remove all alerts

490

self.wb.alerts_remove(priceAlert=True, smartAlert=True)

491

print("All alerts removed")

492

493

# Clear tracking

494

if symbols:

495

for symbol in symbols:

496

self.active_alerts.pop(symbol, None)

497

else:

498

self.active_alerts.clear()

499

500

# Usage

501

alert_mgr = AlertManager(wb)

502

503

# Set up alerts for portfolio stocks

504

portfolio_symbols = ['AAPL', 'TSLA', 'MSFT', 'NVDA', 'AMD']

505

alert_mgr.setup_watchlist_alerts(portfolio_symbols, alert_type='percentage', threshold=3.0)

506

507

# Check alert status

508

alert_mgr.check_alert_status()

509

510

# Later, clean up alerts

511

# alert_mgr.cleanup_alerts(symbols=['AAPL', 'TSLA'])

512

```

513

514

## Complete Screening & Alert Example

515

516

```python

517

from webull import webull

518

import time

519

520

def market_discovery_system():

521

"""Complete market discovery and alert system."""

522

523

wb = webull()

524

wb.login('your_email@example.com', 'your_password')

525

526

# Initialize scanner and alert manager

527

scanner = StockScanner(wb)

528

alert_mgr = AlertManager(wb)

529

530

print("=== MARKET DISCOVERY SYSTEM ===")

531

532

# 1. Run multiple scans

533

print("\n1. Running Market Scans...")

534

535

momentum_stocks = scanner.momentum_scanner()

536

value_stocks = scanner.value_scanner()

537

breakout_stocks = scanner.breakout_scanner()

538

539

# 2. Combine results and find interesting opportunities

540

print("\n2. Analyzing Opportunities...")

541

542

all_candidates = []

543

544

# Add momentum stocks with score

545

for stock in momentum_stocks[:5]:

546

all_candidates.append({

547

**stock,

548

'strategy': 'momentum',

549

'score': stock['changeRatio'] # Use % change as score

550

})

551

552

# Add value stocks with score

553

for stock in value_stocks[:5]:

554

pe_ratio = stock.get('peRatio', 999)

555

score = 100 / pe_ratio if pe_ratio > 0 else 0 # Inverse P/E as score

556

all_candidates.append({

557

**stock,

558

'strategy': 'value',

559

'score': score

560

})

561

562

# Add breakout stocks with score

563

for stock in breakout_stocks[:3]:

564

all_candidates.append({

565

**stock,

566

'strategy': 'breakout',

567

'score': stock['changeRatio'] + (stock['volume'] / 1000000) # Change + volume score

568

})

569

570

# 3. Rank all opportunities

571

all_candidates.sort(key=lambda x: x['score'], reverse=True)

572

573

print("\nTop Opportunities:")

574

for i, stock in enumerate(all_candidates[:10]):

575

print(f"{i+1}. {stock['symbol']} ({stock['strategy']}): Score {stock['score']:.2f}")

576

print(f" ${stock['price']} ({stock['changeRatio']:+.2f}%) Vol: {stock['volume']:,}")

577

578

# 4. Set up alerts for top candidates

579

print("\n3. Setting Up Alerts...")

580

581

top_symbols = [stock['symbol'] for stock in all_candidates[:5]]

582

alert_mgr.setup_watchlist_alerts(top_symbols, alert_type='percentage', threshold=2.0)

583

584

# 5. Monitor for a period

585

print("\n4. Monitoring Market Activity...")

586

587

for minute in range(5): # Monitor for 5 minutes

588

print(f"\nMinute {minute + 1}:")

589

590

# Check current gainers/losers

591

current_gainers = wb.active_gainer_loser('gainer', 'regular', 5)

592

current_losers = wb.active_gainer_loser('loser', 'regular', 5)

593

594

print("Current Top Gainers:")

595

for stock in current_gainers:

596

print(f" {stock['symbol']}: {stock['changeRatio']:+.2f}%")

597

598

print("Current Top Losers:")

599

for stock in current_losers:

600

print(f" {stock['symbol']}: {stock['changeRatio']:+.2f}%")

601

602

time.sleep(60) # Wait 1 minute

603

604

# 6. Final report

605

print("\n5. Final Alert Status:")

606

alert_mgr.check_alert_status()

607

608

# Cleanup

609

print("\nCleaning up alerts...")

610

alert_mgr.cleanup_alerts()

611

612

# Run the system

613

if __name__ == "__main__":

614

market_discovery_system()

615

```

616

617

## Best Practices

618

619

### Alert Management

620

- Use reasonable thresholds to avoid alert fatigue

621

- Review and clean up alerts regularly

622

- Combine price and volume criteria for better signals

623

- Test alert triggers with paper trading first

624

625

### Stock Screening

626

- Use multiple criteria to filter results effectively

627

- Combine fundamental and technical filters

628

- Validate screener results with additional analysis

629

- Save and backtest successful screening strategies

630

631

### Risk Management

632

- Set up alerts for portfolio positions to monitor risk

633

- Use screening to find diversification opportunities

634

- Monitor sector concentration through screening

635

- Set up alerts for major market moves