0
# Data Client
1
2
The Data Client provides comprehensive access to historical and real-time market data across multiple asset classes including stocks, cryptocurrencies, options, and news. It supports both REST API calls for historical data and WebSocket streams for real-time data feeds.
3
4
## Historical Data Clients
5
6
### StockHistoricalDataClient
7
8
```python { .api }
9
from alpaca.data.historical import StockHistoricalDataClient
10
11
class StockHistoricalDataClient(RESTClient):
12
def __init__(
13
self,
14
api_key: Optional[str] = None,
15
secret_key: Optional[str] = None,
16
oauth_token: Optional[str] = None,
17
use_basic_auth: bool = False,
18
raw_data: bool = False,
19
url_override: Optional[str] = None,
20
sandbox: bool = False,
21
) -> None:
22
"""
23
Initialize stock historical data client.
24
25
Args:
26
api_key: Alpaca API key
27
secret_key: Alpaca API secret
28
oauth_token: OAuth token for user-on-behalf access
29
use_basic_auth: Use basic auth headers (for broker sandbox)
30
raw_data: Return raw API responses instead of models
31
url_override: Override base URL for testing
32
sandbox: Use sandbox environment
33
"""
34
```
35
36
#### Basic Setup
37
38
```python { .api }
39
# Standard data client
40
data_client = StockHistoricalDataClient(
41
api_key="your-api-key",
42
secret_key="your-secret-key"
43
)
44
45
# OAuth-based client
46
data_client = StockHistoricalDataClient(
47
oauth_token="user-oauth-token"
48
)
49
50
# Sandbox client (for broker API users)
51
data_client = StockHistoricalDataClient(
52
api_key="broker-sandbox-key",
53
secret_key="broker-sandbox-secret",
54
use_basic_auth=True,
55
sandbox=True
56
)
57
```
58
59
### CryptoHistoricalDataClient
60
61
```python { .api }
62
from alpaca.data.historical import CryptoHistoricalDataClient
63
64
class CryptoHistoricalDataClient(RESTClient):
65
def __init__(
66
self,
67
api_key: Optional[str] = None,
68
secret_key: Optional[str] = None,
69
oauth_token: Optional[str] = None,
70
use_basic_auth: bool = False,
71
raw_data: bool = False,
72
url_override: Optional[str] = None,
73
sandbox: bool = False,
74
) -> None:
75
"""Initialize crypto historical data client with same parameters as stock client."""
76
```
77
78
### OptionHistoricalDataClient
79
80
```python { .api }
81
from alpaca.data.historical import OptionHistoricalDataClient
82
83
class OptionHistoricalDataClient(RESTClient):
84
def __init__(
85
self,
86
api_key: Optional[str] = None,
87
secret_key: Optional[str] = None,
88
oauth_token: Optional[str] = None,
89
use_basic_auth: bool = False,
90
raw_data: bool = False,
91
url_override: Optional[str] = None,
92
sandbox: bool = False,
93
) -> None:
94
"""Initialize options historical data client with same parameters as stock client."""
95
```
96
97
### NewsClient
98
99
```python { .api }
100
from alpaca.data.historical import NewsClient
101
102
class NewsClient(RESTClient):
103
def __init__(
104
self,
105
api_key: Optional[str] = None,
106
secret_key: Optional[str] = None,
107
oauth_token: Optional[str] = None,
108
use_basic_auth: bool = False,
109
raw_data: bool = False,
110
url_override: Optional[str] = None,
111
sandbox: bool = False,
112
) -> None:
113
"""Initialize news data client with same parameters as other clients."""
114
```
115
116
### ScreenerClient
117
118
```python { .api }
119
from alpaca.data.historical import ScreenerClient
120
121
class ScreenerClient(RESTClient):
122
def __init__(
123
self,
124
api_key: Optional[str] = None,
125
secret_key: Optional[str] = None,
126
oauth_token: Optional[str] = None,
127
use_basic_auth: bool = False,
128
raw_data: bool = False,
129
url_override: Optional[str] = None,
130
sandbox: bool = False,
131
) -> None:
132
"""Initialize screener client for market scanning data."""
133
```
134
135
## TimeFrame System
136
137
### TimeFrame Class
138
139
```python { .api }
140
from alpaca.data.timeframe import TimeFrame, TimeFrameUnit
141
142
class TimeFrame:
143
def __init__(self, amount: int, unit: TimeFrameUnit) -> None:
144
"""
145
Create a timeframe for data aggregation.
146
147
Args:
148
amount: Number of time units (1-59 for minutes, 1-23 for hours, etc.)
149
unit: Time unit (Minute, Hour, Day, Week, Month)
150
"""
151
152
@property
153
def amount(self) -> int:
154
"""Number of time units."""
155
156
@property
157
def unit(self) -> TimeFrameUnit:
158
"""Time unit type."""
159
160
@property
161
def value(self) -> str:
162
"""String representation for API (e.g., '5Min', '1Day')."""
163
164
# Predefined timeframes
165
TimeFrame.Minute # 1-minute bars
166
TimeFrame.Hour # 1-hour bars
167
TimeFrame.Day # 1-day bars
168
TimeFrame.Week # 1-week bars
169
TimeFrame.Month # 1-month bars
170
```
171
172
### TimeFrameUnit Enum
173
174
```python { .api }
175
from alpaca.data.timeframe import TimeFrameUnit
176
177
class TimeFrameUnit(str, Enum):
178
Minute = "Min" # Minute intervals (1-59)
179
Hour = "Hour" # Hour intervals (1-23)
180
Day = "Day" # Daily intervals (1 only)
181
Week = "Week" # Weekly intervals (1 only)
182
Month = "Month" # Monthly intervals (1, 2, 3, 6, 12)
183
```
184
185
**Usage Examples:**
186
187
```python { .api }
188
# Create custom timeframes
189
five_min = TimeFrame(5, TimeFrameUnit.Minute)
190
four_hour = TimeFrame(4, TimeFrameUnit.Hour)
191
quarterly = TimeFrame(3, TimeFrameUnit.Month)
192
193
# Use predefined timeframes
194
minute_tf = TimeFrame.Minute
195
daily_tf = TimeFrame.Day
196
197
print(f"Timeframe: {five_min.value}") # Output: "5Min"
198
```
199
200
## Stock Market Data
201
202
### Historical Bars (OHLCV)
203
204
#### get_stock_bars()
205
206
```python { .api }
207
def get_stock_bars(
208
self,
209
request: StockBarsRequest
210
) -> Union[BarSet, RawData]:
211
"""
212
Get historical bar data for stocks.
213
214
Args:
215
request: Bar request parameters
216
217
Returns:
218
BarSet: Time-series bar data with OHLCV information
219
"""
220
```
221
222
#### StockBarsRequest
223
224
```python { .api }
225
from alpaca.data.requests import StockBarsRequest
226
from alpaca.data.timeframe import TimeFrame
227
from alpaca.data.enums import Adjustment, DataFeed
228
from datetime import datetime
229
230
class StockBarsRequest(BaseBarsRequest):
231
def __init__(
232
self,
233
symbol_or_symbols: Union[str, List[str]], # Required: symbols to retrieve
234
timeframe: TimeFrame, # Required: bar aggregation period
235
start: Optional[datetime] = None, # Start time (UTC)
236
end: Optional[datetime] = None, # End time (UTC, defaults to now)
237
limit: Optional[int] = None, # Max bars to return
238
adjustment: Optional[Adjustment] = None, # Corporate action adjustment
239
feed: Optional[DataFeed] = None, # Data feed (IEX, SIP, etc.)
240
sort: Optional[Sort] = None, # ASC or DESC
241
currency: Optional[SupportedCurrencies] = None # Currency (defaults to USD)
242
):
243
```
244
245
#### Adjustment Enum
246
247
```python { .api }
248
from alpaca.data.enums import Adjustment
249
250
class Adjustment(str, Enum):
251
RAW = "raw" # No adjustments
252
SPLIT = "split" # Split-adjusted only
253
DIVIDEND = "dividend" # Dividend-adjusted only
254
ALL = "all" # Both split and dividend adjusted
255
```
256
257
#### DataFeed Enum
258
259
```python { .api }
260
from alpaca.data.enums import DataFeed
261
262
class DataFeed(str, Enum):
263
IEX = "iex" # IEX data feed
264
SIP = "sip" # Securities Information Processor (premium)
265
OTC = "otc" # Over-the-counter data
266
ERSX = "ersx" # ERSX data feed
267
MEMX = "memx" # Members Exchange
268
OPRA = "opra" # Options Price Reporting Authority
269
INDICATIVE = "indicative" # Indicative pricing
270
```
271
272
**Usage Examples:**
273
274
```python { .api }
275
from alpaca.data.requests import StockBarsRequest
276
from alpaca.data.timeframe import TimeFrame, TimeFrameUnit
277
from alpaca.data.enums import Adjustment, DataFeed
278
from datetime import datetime, timedelta
279
280
# Get 1-minute bars for single stock
281
bars_request = StockBarsRequest(
282
symbol_or_symbols="AAPL",
283
timeframe=TimeFrame.Minute,
284
start=datetime.now() - timedelta(hours=6),
285
end=datetime.now()
286
)
287
bars = data_client.get_stock_bars(bars_request)
288
289
# Get daily bars for multiple stocks with adjustments
290
bars_request = StockBarsRequest(
291
symbol_or_symbols=["AAPL", "TSLA", "MSFT", "GOOGL"],
292
timeframe=TimeFrame.Day,
293
start=datetime(2023, 1, 1),
294
end=datetime(2023, 12, 31),
295
adjustment=Adjustment.ALL,
296
feed=DataFeed.SIP
297
)
298
bars = data_client.get_stock_bars(bars_request)
299
300
# Get 5-minute bars with limit
301
bars_request = StockBarsRequest(
302
symbol_or_symbols="SPY",
303
timeframe=TimeFrame(5, TimeFrameUnit.Minute),
304
limit=1000,
305
sort=Sort.DESC # Most recent first
306
)
307
bars = data_client.get_stock_bars(bars_request)
308
```
309
310
#### Bar Model
311
312
```python { .api }
313
from alpaca.data.models import Bar
314
315
class Bar:
316
"""OHLCV bar data for a single time period."""
317
318
symbol: str # Stock symbol
319
timestamp: datetime # Bar timestamp (UTC)
320
open: float # Opening price
321
high: float # High price
322
low: float # Low price
323
close: float # Closing price
324
volume: int # Trading volume
325
trade_count: Optional[int] # Number of trades
326
vwap: Optional[float] # Volume-weighted average price
327
```
328
329
#### BarSet Model
330
331
```python { .api }
332
from alpaca.data.models import BarSet
333
334
class BarSet:
335
"""Collection of bars with time-series functionality."""
336
337
# Dictionary access: bars["AAPL"] returns List[Bar]
338
# Pandas integration: bars.df returns DataFrame
339
# Time-series operations available
340
341
def __getitem__(self, symbol: str) -> List[Bar]:
342
"""Get bars for specific symbol."""
343
344
@property
345
def df(self) -> pd.DataFrame:
346
"""Convert to pandas DataFrame with MultiIndex."""
347
348
@property
349
def symbols(self) -> List[str]:
350
"""Get all symbols in the bar set."""
351
```
352
353
**Working with Bar Data:**
354
355
```python { .api }
356
# Access bars by symbol
357
bars = data_client.get_stock_bars(bars_request)
358
aapl_bars = bars["AAPL"]
359
360
print(f"Retrieved {len(aapl_bars)} bars for AAPL")
361
for bar in aapl_bars[-5:]: # Last 5 bars
362
print(f"{bar.timestamp}: O:{bar.open} H:{bar.high} L:{bar.low} C:{bar.close} V:{bar.volume}")
363
364
# Convert to pandas for analysis
365
df = bars.df
366
print(df.head())
367
print(f"Symbols: {bars.symbols}")
368
369
# Calculate returns
370
df['returns'] = df.groupby('symbol')['close'].pct_change()
371
print(f"Average daily return: {df.groupby('symbol')['returns'].mean()}")
372
```
373
374
### Quotes (Bid/Ask Data)
375
376
#### get_stock_quotes()
377
378
```python { .api }
379
def get_stock_quotes(
380
self,
381
request: StockQuotesRequest
382
) -> Union[QuoteSet, RawData]:
383
"""
384
Get historical quote data (bid/ask) for stocks.
385
386
Args:
387
request: Quote request parameters
388
389
Returns:
390
QuoteSet: Time-series quote data
391
"""
392
```
393
394
#### StockQuotesRequest
395
396
```python { .api }
397
from alpaca.data.requests import StockQuotesRequest
398
399
class StockQuotesRequest(BaseTimeseriesDataRequest):
400
def __init__(
401
self,
402
symbol_or_symbols: Union[str, List[str]], # Required: symbols
403
start: Optional[datetime] = None, # Start time (UTC)
404
end: Optional[datetime] = None, # End time (UTC)
405
limit: Optional[int] = None, # Max quotes to return
406
feed: Optional[DataFeed] = None, # Data feed
407
sort: Optional[Sort] = None, # Sort order
408
currency: Optional[SupportedCurrencies] = None
409
):
410
```
411
412
#### Quote Model
413
414
```python { .api }
415
from alpaca.data.models import Quote
416
417
class Quote:
418
"""Bid/ask quote data."""
419
420
symbol: str # Stock symbol
421
timestamp: datetime # Quote timestamp (UTC)
422
bid_exchange: str # Bid exchange code
423
bid_price: float # Bid price
424
bid_size: int # Bid size (shares)
425
ask_exchange: str # Ask exchange code
426
ask_price: float # Ask price
427
ask_size: int # Ask size (shares)
428
conditions: Optional[List[str]] # Quote conditions/flags
429
```
430
431
**Usage Examples:**
432
433
```python { .api }
434
# Get recent quotes
435
quotes_request = StockQuotesRequest(
436
symbol_or_symbols="AAPL",
437
start=datetime.now() - timedelta(hours=1),
438
limit=1000
439
)
440
quotes = data_client.get_stock_quotes(quotes_request)
441
442
# Analyze bid-ask spread
443
aapl_quotes = quotes["AAPL"]
444
for quote in aapl_quotes[-10:]:
445
spread = quote.ask_price - quote.bid_price
446
spread_pct = (spread / quote.ask_price) * 100
447
print(f"{quote.timestamp}: Bid ${quote.bid_price} Ask ${quote.ask_price} Spread {spread_pct:.3f}%")
448
```
449
450
### Trades (Individual Transactions)
451
452
#### get_stock_trades()
453
454
```python { .api }
455
def get_stock_trades(
456
self,
457
request: StockTradesRequest
458
) -> Union[TradeSet, RawData]:
459
"""
460
Get historical trade data for stocks.
461
462
Args:
463
request: Trade request parameters
464
465
Returns:
466
TradeSet: Time-series trade execution data
467
"""
468
```
469
470
#### StockTradesRequest
471
472
```python { .api }
473
from alpaca.data.requests import StockTradesRequest
474
475
class StockTradesRequest(BaseTimeseriesDataRequest):
476
def __init__(
477
self,
478
symbol_or_symbols: Union[str, List[str]], # Required: symbols
479
start: Optional[datetime] = None, # Start time (UTC)
480
end: Optional[datetime] = None, # End time (UTC)
481
limit: Optional[int] = None, # Max trades to return
482
feed: Optional[DataFeed] = None, # Data feed
483
sort: Optional[Sort] = None, # Sort order
484
currency: Optional[SupportedCurrencies] = None
485
):
486
```
487
488
#### Trade Model
489
490
```python { .api }
491
from alpaca.data.models import Trade
492
493
class Trade:
494
"""Individual trade execution data."""
495
496
symbol: str # Stock symbol
497
timestamp: datetime # Trade timestamp (UTC)
498
exchange: str # Exchange code
499
price: float # Trade price
500
size: int # Trade size (shares)
501
trade_id: Optional[str] # Unique trade ID
502
conditions: Optional[List[str]] # Trade conditions/flags
503
```
504
505
**Usage Examples:**
506
507
```python { .api }
508
# Get recent trades for analysis
509
trades_request = StockTradesRequest(
510
symbol_or_symbols="TSLA",
511
start=datetime.now() - timedelta(minutes=30),
512
limit=5000
513
)
514
trades = data_client.get_stock_trades(trades_request)
515
516
# Analyze trade activity
517
tsla_trades = trades["TSLA"]
518
total_volume = sum(trade.size for trade in tsla_trades)
519
vwap = sum(trade.price * trade.size for trade in tsla_trades) / total_volume
520
521
print(f"Trades: {len(tsla_trades)}, Volume: {total_volume:,}, VWAP: ${vwap:.2f}")
522
```
523
524
### Latest Market Data
525
526
#### get_stock_latest_bar()
527
528
```python { .api }
529
def get_stock_latest_bar(
530
self,
531
request: StockLatestBarRequest
532
) -> Union[Dict[str, Bar], RawData]:
533
"""
534
Get latest bar for stocks.
535
536
Args:
537
request: Latest bar request
538
539
Returns:
540
Dict[str, Bar]: Latest bar for each symbol
541
"""
542
```
543
544
#### get_stock_latest_quote()
545
546
```python { .api }
547
def get_stock_latest_quote(
548
self,
549
request: StockLatestQuoteRequest
550
) -> Union[Dict[str, Quote], RawData]:
551
"""
552
Get latest quote for stocks.
553
554
Args:
555
request: Latest quote request
556
557
Returns:
558
Dict[str, Quote]: Latest quote for each symbol
559
"""
560
```
561
562
#### get_stock_latest_trade()
563
564
```python { .api }
565
def get_stock_latest_trade(
566
self,
567
request: StockLatestTradeRequest
568
) -> Union[Dict[str, Trade], RawData]:
569
"""
570
Get latest trade for stocks.
571
572
Args:
573
request: Latest trade request
574
575
Returns:
576
Dict[str, Trade]: Latest trade for each symbol
577
"""
578
```
579
580
#### Latest Data Request Classes
581
582
```python { .api }
583
from alpaca.data.requests import (
584
StockLatestBarRequest, StockLatestQuoteRequest, StockLatestTradeRequest
585
)
586
587
class StockLatestBarRequest(NonEmptyRequest):
588
def __init__(
589
self,
590
symbol_or_symbols: Union[str, List[str]], # Required: symbols
591
feed: Optional[DataFeed] = None, # Data feed
592
currency: Optional[SupportedCurrencies] = None
593
):
594
595
class StockLatestQuoteRequest(NonEmptyRequest):
596
def __init__(
597
self,
598
symbol_or_symbols: Union[str, List[str]], # Required: symbols
599
feed: Optional[DataFeed] = None, # Data feed
600
currency: Optional[SupportedCurrencies] = None
601
):
602
603
class StockLatestTradeRequest(NonEmptyRequest):
604
def __init__(
605
self,
606
symbol_or_symbols: Union[str, List[str]], # Required: symbols
607
feed: Optional[DataFeed] = None, # Data feed
608
currency: Optional[SupportedCurrencies] = None
609
):
610
```
611
612
**Usage Examples:**
613
614
```python { .api }
615
# Get latest bars for portfolio monitoring
616
latest_bars = data_client.get_stock_latest_bar(
617
StockLatestBarRequest(
618
symbol_or_symbols=["AAPL", "TSLA", "MSFT", "GOOGL"],
619
feed=DataFeed.IEX
620
)
621
)
622
623
for symbol, bar in latest_bars.items():
624
print(f"{symbol}: ${bar.close} (Vol: {bar.volume:,})")
625
626
# Get latest quotes for order placement
627
latest_quotes = data_client.get_stock_latest_quote(
628
StockLatestQuoteRequest(symbol_or_symbols="AAPL")
629
)
630
631
aapl_quote = latest_quotes["AAPL"]
632
print(f"AAPL Bid: ${aapl_quote.bid_price} x {aapl_quote.bid_size}")
633
print(f"AAPL Ask: ${aapl_quote.ask_price} x {aapl_quote.ask_size}")
634
635
# Get latest trades for execution analysis
636
latest_trades = data_client.get_stock_latest_trade(
637
StockLatestTradeRequest(symbol_or_symbols="SPY")
638
)
639
640
spy_trade = latest_trades["SPY"]
641
print(f"SPY Last: ${spy_trade.price} @ {spy_trade.timestamp}")
642
```
643
644
### Market Snapshots
645
646
#### get_stock_snapshot()
647
648
```python { .api }
649
def get_stock_snapshot(
650
self,
651
request: StockSnapshotRequest
652
) -> Union[Dict[str, Snapshot], RawData]:
653
"""
654
Get comprehensive market snapshot for stocks.
655
656
Args:
657
request: Snapshot request parameters
658
659
Returns:
660
Dict[str, Snapshot]: Complete market data snapshot for each symbol
661
"""
662
```
663
664
#### StockSnapshotRequest
665
666
```python { .api }
667
from alpaca.data.requests import StockSnapshotRequest
668
669
class StockSnapshotRequest(NonEmptyRequest):
670
def __init__(
671
self,
672
symbol_or_symbols: Union[str, List[str]], # Required: symbols
673
feed: Optional[DataFeed] = None, # Data feed
674
currency: Optional[SupportedCurrencies] = None
675
):
676
```
677
678
#### Snapshot Model
679
680
```python { .api }
681
from alpaca.data.models import Snapshot
682
683
class Snapshot:
684
"""Comprehensive market snapshot combining all data types."""
685
686
symbol: str # Stock symbol
687
latest_trade: Optional[Trade] # Most recent trade
688
latest_quote: Optional[Quote] # Current bid/ask
689
minute_bar: Optional[Bar] # Current minute bar
690
daily_bar: Optional[Bar] # Current daily bar
691
prev_daily_bar: Optional[Bar] # Previous day's bar
692
```
693
694
**Usage Examples:**
695
696
```python { .api }
697
# Get comprehensive market snapshot
698
snapshot = data_client.get_stock_snapshot(
699
StockSnapshotRequest(
700
symbol_or_symbols=["AAPL", "TSLA", "SPY"],
701
feed=DataFeed.IEX
702
)
703
)
704
705
for symbol, snap in snapshot.items():
706
print(f"\n{symbol} Snapshot:")
707
708
if snap.latest_trade:
709
print(f" Last Trade: ${snap.latest_trade.price} @ {snap.latest_trade.timestamp}")
710
711
if snap.latest_quote:
712
spread = snap.latest_quote.ask_price - snap.latest_quote.bid_price
713
print(f" Quote: ${snap.latest_quote.bid_price} x ${snap.latest_quote.ask_price} (${spread:.2f})")
714
715
if snap.daily_bar and snap.prev_daily_bar:
716
daily_change = snap.daily_bar.close - snap.prev_daily_bar.close
717
daily_change_pct = (daily_change / snap.prev_daily_bar.close) * 100
718
print(f" Daily: ${snap.daily_bar.close} ({daily_change_pct:+.2f}%)")
719
print(f" Volume: {snap.daily_bar.volume:,}")
720
```
721
722
## Cryptocurrency Data
723
724
The cryptocurrency clients follow the same patterns as stock data with crypto-specific symbols and features.
725
726
### CryptoHistoricalDataClient Methods
727
728
```python { .api }
729
from alpaca.data.historical import CryptoHistoricalDataClient
730
from alpaca.data.requests import (
731
CryptoBarsRequest, CryptoQuotesRequest, CryptoTradesRequest,
732
CryptoLatestBarRequest, CryptoLatestQuoteRequest, CryptoLatestTradeRequest,
733
CryptoSnapshotRequest, CryptoLatestOrderbookRequest
734
)
735
736
crypto_client = CryptoHistoricalDataClient(api_key="key", secret_key="secret")
737
738
# Same methods as stock client but with crypto request types
739
crypto_client.get_crypto_bars(CryptoBarsRequest(...))
740
crypto_client.get_crypto_quotes(CryptoQuotesRequest(...))
741
crypto_client.get_crypto_trades(CryptoTradesRequest(...))
742
crypto_client.get_crypto_latest_bar(CryptoLatestBarRequest(...))
743
crypto_client.get_crypto_latest_quote(CryptoLatestQuoteRequest(...))
744
crypto_client.get_crypto_latest_trade(CryptoLatestTradeRequest(...))
745
crypto_client.get_crypto_snapshot(CryptoSnapshotRequest(...))
746
747
# Crypto-specific: order book data
748
crypto_client.get_crypto_latest_orderbook(CryptoLatestOrderbookRequest(...))
749
```
750
751
### Crypto Symbols and Usage
752
753
```python { .api }
754
# Crypto symbols use / format: BASE/QUOTE
755
crypto_symbols = ["BTC/USD", "ETH/USD", "LTC/USD", "BCH/USD", "DOGE/USD"]
756
757
# Get crypto bars
758
crypto_bars = crypto_client.get_crypto_bars(
759
CryptoBarsRequest(
760
symbol_or_symbols=crypto_symbols,
761
timeframe=TimeFrame.Hour,
762
start=datetime.now() - timedelta(days=7)
763
)
764
)
765
766
# Get latest crypto quotes
767
crypto_quotes = crypto_client.get_crypto_latest_quote(
768
CryptoLatestQuoteRequest(symbol_or_symbols="BTC/USD")
769
)
770
771
btc_quote = crypto_quotes["BTC/USD"]
772
print(f"BTC/USD: ${btc_quote.bid_price:.2f} / ${btc_quote.ask_price:.2f}")
773
```
774
775
### Crypto Order Book Data
776
777
```python { .api }
778
from alpaca.data.requests import CryptoLatestOrderbookRequest
779
780
# Get order book depth
781
orderbook = crypto_client.get_crypto_latest_orderbook(
782
CryptoLatestOrderbookRequest(symbol_or_symbols="BTC/USD")
783
)
784
785
btc_book = orderbook["BTC/USD"]
786
print("Top 5 Bids:")
787
for bid in btc_book.bids[:5]:
788
print(f" ${bid.price:.2f} x {bid.size}")
789
790
print("Top 5 Asks:")
791
for ask in btc_book.asks[:5]:
792
print(f" ${ask.price:.2f} x {ask.size}")
793
```
794
795
## Options Data
796
797
### OptionHistoricalDataClient Methods
798
799
```python { .api }
800
from alpaca.data.historical import OptionHistoricalDataClient
801
from alpaca.data.requests import (
802
OptionBarsRequest, OptionTradesRequest,
803
OptionLatestQuoteRequest, OptionLatestTradeRequest,
804
OptionSnapshotRequest, OptionChainRequest
805
)
806
807
options_client = OptionHistoricalDataClient(api_key="key", secret_key="secret")
808
809
# Options data methods
810
options_client.get_option_bars(OptionBarsRequest(...))
811
options_client.get_option_trades(OptionTradesRequest(...))
812
options_client.get_option_latest_quote(OptionLatestQuoteRequest(...))
813
options_client.get_option_latest_trade(OptionLatestTradeRequest(...))
814
options_client.get_option_snapshot(OptionSnapshotRequest(...))
815
816
# Options-specific: option chain data
817
options_client.get_option_chain(OptionChainRequest(...))
818
```
819
820
### Option Data Usage
821
822
```python { .api }
823
# Option symbols use OCC format: AAPL230317C00150000
824
# Format: [SYMBOL][YYMMDD][C/P][STRIKE_PRICE_PADDED]
825
826
# Get option bars
827
option_bars = options_client.get_option_bars(
828
OptionBarsRequest(
829
symbol_or_symbols="AAPL230317C00150000", # AAPL $150 Call expiring 3/17/23
830
timeframe=TimeFrame.Minute,
831
start=datetime.now() - timedelta(hours=6)
832
)
833
)
834
835
# Get option chain for underlying
836
option_chain = options_client.get_option_chain(
837
OptionChainRequest(
838
underlying_symbol="AAPL",
839
expiration_date=date(2023, 3, 17)
840
)
841
)
842
843
print("AAPL Option Chain 3/17/23:")
844
for contract in option_chain.options:
845
print(f"{contract.symbol}: ${contract.strike_price} {contract.type}")
846
```
847
848
## News Data
849
850
### News Client
851
852
```python { .api }
853
from alpaca.data.historical import NewsClient
854
from alpaca.data.requests import NewsRequest
855
856
news_client = NewsClient(api_key="key", secret_key="secret")
857
858
def get_news(self, request: NewsRequest) -> Union[NewsSet, RawData]:
859
"""
860
Get news articles with filtering.
861
862
Args:
863
request: News query parameters
864
865
Returns:
866
NewsSet: Collection of news articles
867
"""
868
```
869
870
### NewsRequest
871
872
```python { .api }
873
from alpaca.data.requests import NewsRequest
874
875
class NewsRequest(NonEmptyRequest):
876
def __init__(
877
self,
878
symbols: Optional[Union[str, List[str]]] = None, # Filter by symbols
879
start: Optional[datetime] = None, # Start time
880
end: Optional[datetime] = None, # End time
881
sort: Optional[Sort] = None, # Sort order
882
include_content: Optional[bool] = None, # Include article body
883
exclude_contentless: Optional[bool] = None, # Exclude articles without content
884
limit: Optional[int] = None # Max articles to return
885
):
886
```
887
888
### News Model
889
890
```python { .api }
891
from alpaca.data.models import News
892
893
class News:
894
"""News article information."""
895
896
id: str # Article ID
897
headline: str # Article headline
898
author: Optional[str] # Article author
899
created_at: datetime # Publication timestamp
900
updated_at: datetime # Last update timestamp
901
summary: Optional[str] # Article summary
902
content: Optional[str] # Full article content (if requested)
903
symbols: List[str] # Related symbols
904
url: Optional[str] # Source URL
905
images: Optional[List[dict]] # Article images
906
```
907
908
**Usage Examples:**
909
910
```python { .api }
911
# Get recent news for specific stocks
912
news = news_client.get_news(
913
NewsRequest(
914
symbols=["AAPL", "TSLA", "MSFT"],
915
start=datetime.now() - timedelta(hours=24),
916
limit=50,
917
include_content=True
918
)
919
)
920
921
print(f"Found {len(news.news)} articles")
922
for article in news.news[:5]:
923
print(f"\n{article.headline}")
924
print(f"Symbols: {', '.join(article.symbols)}")
925
print(f"Published: {article.created_at}")
926
if article.summary:
927
print(f"Summary: {article.summary[:200]}...")
928
929
# Get all market news (no symbol filter)
930
market_news = news_client.get_news(
931
NewsRequest(
932
start=datetime.now() - timedelta(hours=6),
933
sort=Sort.DESC,
934
limit=20
935
)
936
)
937
```
938
939
## Market Screening
940
941
### ScreenerClient
942
943
```python { .api }
944
from alpaca.data.historical import ScreenerClient
945
from alpaca.data.requests import MostActivesRequest, MarketMoversRequest
946
947
screener_client = ScreenerClient(api_key="key", secret_key="secret")
948
949
def get_most_actives(
950
self,
951
request: MostActivesRequest
952
) -> Union[MostActives, RawData]:
953
"""
954
Get most active stocks by volume or trade count.
955
956
Args:
957
request: Most actives query parameters
958
959
Returns:
960
MostActives: Most active stocks data
961
"""
962
963
def get_market_movers(
964
self,
965
request: MarketMoversRequest
966
) -> Union[Movers, RawData]:
967
"""
968
Get top gaining and losing stocks.
969
970
Args:
971
request: Market movers query parameters
972
973
Returns:
974
Movers: Top movers data with gainers and losers
975
"""
976
```
977
978
### Screener Request Classes
979
980
```python { .api }
981
from alpaca.data.requests import MostActivesRequest, MarketMoversRequest
982
from alpaca.data.enums import MostActivesBy, MarketType
983
984
class MostActivesRequest(NonEmptyRequest):
985
def __init__(
986
self,
987
by: MostActivesBy, # VOLUME or TRADE_COUNT
988
top: int, # Number of stocks to return (max 50)
989
market_type: Optional[MarketType] = None # STOCKS (default)
990
):
991
992
class MarketMoversRequest(NonEmptyRequest):
993
def __init__(
994
self,
995
top: int, # Number of gainers/losers (max 50)
996
market_type: Optional[MarketType] = None # STOCKS (default)
997
):
998
```
999
1000
**Usage Examples:**
1001
1002
```python { .api }
1003
from alpaca.data.enums import MostActivesBy
1004
1005
# Get most active stocks by volume
1006
most_active = screener_client.get_most_actives(
1007
MostActivesRequest(by=MostActivesBy.VOLUME, top=20)
1008
)
1009
1010
print("Most Active Stocks by Volume:")
1011
for stock in most_active.most_actives:
1012
print(f"{stock.symbol}: {stock.volume:,} shares, ${stock.price:.2f}")
1013
1014
# Get top movers (gainers and losers)
1015
movers = screener_client.get_market_movers(
1016
MarketMoversRequest(top=10)
1017
)
1018
1019
print("\nTop Gainers:")
1020
for gainer in movers.gainers:
1021
print(f"{gainer.symbol}: +{gainer.percent_change:.2f}% (${gainer.price:.2f})")
1022
1023
print("\nTop Losers:")
1024
for loser in movers.losers:
1025
print(f"{loser.symbol}: {loser.percent_change:.2f}% (${loser.price:.2f})")
1026
```
1027
1028
## Real-time Data Streaming
1029
1030
### Stock Data Stream
1031
1032
```python { .api }
1033
from alpaca.data.live import StockDataStream
1034
1035
class StockDataStream:
1036
def __init__(
1037
self,
1038
api_key: Optional[str] = None,
1039
secret_key: Optional[str] = None,
1040
raw_data: bool = False,
1041
feed: Optional[DataFeed] = None, # IEX, SIP, etc.
1042
websocket_params: Optional[dict] = None,
1043
url_override: Optional[str] = None
1044
):
1045
"""
1046
Initialize stock data stream for real-time data.
1047
1048
Args:
1049
api_key: API key for authentication
1050
secret_key: Secret key for authentication
1051
raw_data: Return raw data instead of models
1052
feed: Data feed selection
1053
websocket_params: WebSocket configuration
1054
url_override: Override WebSocket URL
1055
"""
1056
1057
def subscribe_bars(self, handler: Callable, *symbols) -> None:
1058
"""Subscribe to real-time bar updates."""
1059
1060
def subscribe_quotes(self, handler: Callable, *symbols) -> None:
1061
"""Subscribe to real-time quote updates."""
1062
1063
def subscribe_trades(self, handler: Callable, *symbols) -> None:
1064
"""Subscribe to real-time trade updates."""
1065
1066
def subscribe_updated_bars(self, handler: Callable, *symbols) -> None:
1067
"""Subscribe to minute bar updates."""
1068
1069
def subscribe_daily_bars(self, handler: Callable, *symbols) -> None:
1070
"""Subscribe to daily bar updates."""
1071
1072
def subscribe_statuses(self, handler: Callable, *symbols) -> None:
1073
"""Subscribe to trading status updates."""
1074
1075
def subscribe_lulds(self, handler: Callable, *symbols) -> None:
1076
"""Subscribe to limit up/limit down updates."""
1077
1078
def subscribe_corrections(self, handler: Callable, *symbols) -> None:
1079
"""Subscribe to trade corrections."""
1080
1081
def subscribe_cancellations(self, handler: Callable, *symbols) -> None:
1082
"""Subscribe to trade cancellations."""
1083
1084
async def run(self) -> None:
1085
"""Start the WebSocket connection and begin streaming."""
1086
```
1087
1088
### Real-time Data Example
1089
1090
```python { .api }
1091
import asyncio
1092
from alpaca.data.live import StockDataStream
1093
from alpaca.data.enums import DataFeed
1094
1095
# Initialize stream
1096
stream = StockDataStream(
1097
api_key="your-api-key",
1098
secret_key="your-secret-key",
1099
feed=DataFeed.IEX
1100
)
1101
1102
# Define data handlers
1103
async def trade_handler(data):
1104
"""Handle real-time trade data."""
1105
print(f"TRADE {data.symbol}: ${data.price} x {data.size} @ {data.timestamp}")
1106
1107
async def quote_handler(data):
1108
"""Handle real-time quote data."""
1109
spread = data.ask_price - data.bid_price
1110
print(f"QUOTE {data.symbol}: ${data.bid_price} x {data.ask_price} (${spread:.2f})")
1111
1112
async def bar_handler(data):
1113
"""Handle real-time bar data."""
1114
print(f"BAR {data.symbol}: O:${data.open} H:${data.high} L:${data.low} C:${data.close} V:{data.volume}")
1115
1116
# Subscribe to data streams
1117
symbols = ["AAPL", "TSLA", "SPY", "QQQ"]
1118
1119
stream.subscribe_trades(trade_handler, *symbols)
1120
stream.subscribe_quotes(quote_handler, *symbols)
1121
stream.subscribe_updated_bars(bar_handler, *symbols)
1122
1123
# Start streaming (this will run indefinitely)
1124
asyncio.run(stream.run())
1125
```
1126
1127
### Crypto Data Stream
1128
1129
```python { .api }
1130
from alpaca.data.live import CryptoDataStream
1131
1132
# Similar interface to stock stream but for crypto symbols
1133
crypto_stream = CryptoDataStream(
1134
api_key="your-api-key",
1135
secret_key="your-secret-key"
1136
)
1137
1138
# Subscribe to crypto data
1139
crypto_symbols = ["BTC/USD", "ETH/USD", "DOGE/USD"]
1140
crypto_stream.subscribe_trades(trade_handler, *crypto_symbols)
1141
crypto_stream.subscribe_quotes(quote_handler, *crypto_symbols)
1142
crypto_stream.subscribe_bars(bar_handler, *crypto_symbols)
1143
1144
# Crypto-specific: order book updates
1145
async def orderbook_handler(data):
1146
print(f"ORDERBOOK {data.symbol}: {len(data.bids)} bids, {len(data.asks)} asks")
1147
1148
crypto_stream.subscribe_orderbooks(orderbook_handler, "BTC/USD")
1149
```
1150
1151
### Option Data Stream
1152
1153
```python { .api }
1154
from alpaca.data.live import OptionDataStream
1155
1156
# Stream real-time options data
1157
option_stream = OptionDataStream(
1158
api_key="your-api-key",
1159
secret_key="your-secret-key"
1160
)
1161
1162
async def option_trade_handler(data):
1163
"""Handle real-time option trade data."""
1164
print(f"OPTION TRADE {data.symbol}: ${data.price} x {data.size}")
1165
1166
async def option_quote_handler(data):
1167
"""Handle real-time option quote data."""
1168
print(f"OPTION QUOTE {data.symbol}: ${data.bid_price} x ${data.ask_price}")
1169
1170
# Subscribe to option data
1171
option_symbols = ["AAPL230317C00150000", "TSLA230317P00200000"]
1172
option_stream.subscribe_trades(option_trade_handler, *option_symbols)
1173
option_stream.subscribe_quotes(option_quote_handler, *option_symbols)
1174
```
1175
1176
### News Data Stream
1177
1178
```python { .api }
1179
from alpaca.data.live import NewsDataStream
1180
1181
# Stream real-time news
1182
news_stream = NewsDataStream(
1183
api_key="your-api-key",
1184
secret_key="your-secret-key"
1185
)
1186
1187
async def news_handler(data):
1188
"""Handle real-time news."""
1189
print(f"NEWS: {data.headline}")
1190
print(f"Symbols: {', '.join(data.symbols)}")
1191
print(f"Time: {data.created_at}")
1192
1193
# Subscribe to news for specific symbols
1194
news_stream.subscribe_news(news_handler, "AAPL", "TSLA", "MSFT")
1195
1196
# Or subscribe to all news (no symbol filter)
1197
news_stream.subscribe_news(news_handler)
1198
```
1199
1200
## Advanced Streaming Example
1201
1202
```python { .api }
1203
import asyncio
1204
from datetime import datetime
1205
from collections import defaultdict
1206
from alpaca.data.live import StockDataStream, NewsDataStream
1207
from alpaca.data.enums import DataFeed
1208
1209
class RealTimeAnalyzer:
1210
def __init__(self, api_key: str, secret_key: str):
1211
# Initialize data streams
1212
self.data_stream = StockDataStream(
1213
api_key=api_key,
1214
secret_key=secret_key,
1215
feed=DataFeed.IEX
1216
)
1217
1218
self.news_stream = NewsDataStream(
1219
api_key=api_key,
1220
secret_key=secret_key
1221
)
1222
1223
# Data storage
1224
self.latest_prices = {}
1225
self.trade_volumes = defaultdict(int)
1226
self.price_alerts = {
1227
"AAPL": {"above": 180.00, "below": 170.00},
1228
"TSLA": {"above": 250.00, "below": 200.00}
1229
}
1230
1231
# Subscribe to streams
1232
self.setup_subscriptions()
1233
1234
def setup_subscriptions(self):
1235
"""Set up data stream subscriptions."""
1236
symbols = ["AAPL", "TSLA", "SPY", "QQQ", "MSFT"]
1237
1238
# Market data subscriptions
1239
self.data_stream.subscribe_trades(self.handle_trade, *symbols)
1240
self.data_stream.subscribe_quotes(self.handle_quote, *symbols)
1241
self.data_stream.subscribe_updated_bars(self.handle_bar, *symbols)
1242
1243
# News subscriptions
1244
self.news_stream.subscribe_news(self.handle_news, *symbols)
1245
1246
async def handle_trade(self, trade):
1247
"""Process real-time trade data."""
1248
symbol = trade.symbol
1249
self.latest_prices[symbol] = trade.price
1250
self.trade_volumes[symbol] += trade.size
1251
1252
# Check price alerts
1253
if symbol in self.price_alerts:
1254
alerts = self.price_alerts[symbol]
1255
if trade.price > alerts["above"]:
1256
print(f"š {symbol} ABOVE ${alerts['above']}: ${trade.price}")
1257
elif trade.price < alerts["below"]:
1258
print(f"š {symbol} BELOW ${alerts['below']}: ${trade.price}")
1259
1260
# Print large trades
1261
if trade.size >= 10000:
1262
print(f"š LARGE TRADE {symbol}: ${trade.price} x {trade.size:,}")
1263
1264
async def handle_quote(self, quote):
1265
"""Process real-time quote data."""
1266
spread = quote.ask_price - quote.bid_price
1267
spread_pct = (spread / quote.ask_price) * 100
1268
1269
# Alert on wide spreads
1270
if spread_pct > 0.5: # 0.5% spread
1271
print(f"ā ļø WIDE SPREAD {quote.symbol}: {spread_pct:.2f}%")
1272
1273
async def handle_bar(self, bar):
1274
"""Process real-time bar updates."""
1275
# Calculate intraday returns
1276
if bar.symbol in self.latest_prices:
1277
prev_price = self.latest_prices.get(f"{bar.symbol}_prev", bar.open)
1278
change_pct = ((bar.close - prev_price) / prev_price) * 100
1279
1280
if abs(change_pct) > 2.0: # 2% move
1281
direction = "UP" if change_pct > 0 else "DOWN"
1282
print(f"š {bar.symbol} {direction} {change_pct:+.2f}% - ${bar.close} (Vol: {bar.volume:,})")
1283
1284
self.latest_prices[f"{bar.symbol}_prev"] = bar.close
1285
1286
async def handle_news(self, news):
1287
"""Process real-time news."""
1288
print(f"š° NEWS: {news.headline}")
1289
print(f" Symbols: {', '.join(news.symbols)}")
1290
print(f" Time: {news.created_at}")
1291
1292
# Check for important keywords
1293
headline_lower = news.headline.lower()
1294
important_keywords = ["earnings", "merger", "acquisition", "fda approval", "bankruptcy"]
1295
1296
for keyword in important_keywords:
1297
if keyword in headline_lower:
1298
print(f"š„ IMPORTANT: {keyword.upper()} mentioned in news!")
1299
break
1300
1301
async def print_summary(self):
1302
"""Print periodic summary."""
1303
while True:
1304
await asyncio.sleep(60) # Every minute
1305
1306
print(f"\n=== MARKET SUMMARY ({datetime.now().strftime('%H:%M:%S')}) ===")
1307
for symbol, price in self.latest_prices.items():
1308
if not symbol.endswith("_prev"):
1309
volume = self.trade_volumes.get(symbol, 0)
1310
print(f"{symbol}: ${price:.2f} (Vol: {volume:,})")
1311
print("=" * 50)
1312
1313
async def run(self):
1314
"""Start the real-time analyzer."""
1315
print("š Starting real-time market analyzer...")
1316
1317
# Start data streams
1318
data_task = asyncio.create_task(self.data_stream.run())
1319
news_task = asyncio.create_task(self.news_stream.run())
1320
summary_task = asyncio.create_task(self.print_summary())
1321
1322
try:
1323
# Run all tasks concurrently
1324
await asyncio.gather(data_task, news_task, summary_task)
1325
except KeyboardInterrupt:
1326
print("\nš Shutting down analyzer...")
1327
data_task.cancel()
1328
news_task.cancel()
1329
summary_task.cancel()
1330
1331
# Run the analyzer
1332
if __name__ == "__main__":
1333
analyzer = RealTimeAnalyzer(
1334
api_key="your-api-key",
1335
secret_key="your-secret-key"
1336
)
1337
1338
asyncio.run(analyzer.run())
1339
```
1340
1341
## Data Processing Utilities
1342
1343
### Converting to Pandas DataFrames
1344
1345
```python { .api }
1346
import pandas as pd
1347
1348
# BarSet, QuoteSet, TradeSet all have .df property
1349
bars = data_client.get_stock_bars(bars_request)
1350
df = bars.df
1351
1352
# DataFrame has MultiIndex: (timestamp, symbol)
1353
print(df.index.names) # ['timestamp', 'symbol']
1354
print(df.columns) # ['open', 'high', 'low', 'close', 'volume', ...]
1355
1356
# Access data for specific symbol
1357
aapl_data = df.xs('AAPL', level='symbol')
1358
print(aapl_data.tail())
1359
1360
# Pivot for time-series analysis
1361
pivot_df = df.reset_index().pivot(index='timestamp', columns='symbol', values='close')
1362
print(pivot_df.head())
1363
1364
# Calculate returns
1365
returns = pivot_df.pct_change().dropna()
1366
print(f"Correlations:\n{returns.corr()}")
1367
```
1368
1369
### Data Export and Storage
1370
1371
```python { .api }
1372
# Export to CSV
1373
bars_df = bars.df
1374
bars_df.to_csv('market_data.csv')
1375
1376
# Export to Parquet (more efficient for large datasets)
1377
bars_df.to_parquet('market_data.parquet')
1378
1379
# Store in database (example with SQLite)
1380
import sqlite3
1381
1382
conn = sqlite3.connect('market_data.db')
1383
bars_df.to_sql('stock_bars', conn, if_exists='append', index=True)
1384
conn.close()
1385
1386
# Export individual symbols
1387
for symbol in bars.symbols:
1388
symbol_bars = bars[symbol]
1389
symbol_df = pd.DataFrame([{
1390
'timestamp': bar.timestamp,
1391
'open': bar.open,
1392
'high': bar.high,
1393
'low': bar.low,
1394
'close': bar.close,
1395
'volume': bar.volume
1396
} for bar in symbol_bars])
1397
symbol_df.to_csv(f'{symbol}_bars.csv', index=False)
1398
```
1399
1400
## Common Types and Base Classes
1401
1402
### Base Request Types
1403
1404
```python { .api }
1405
from alpaca.common.requests import NonEmptyRequest
1406
from alpaca.common.enums import Sort, SupportedCurrencies
1407
1408
class NonEmptyRequest:
1409
"""Base class for API request objects with validation."""
1410
1411
class Sort(str, Enum):
1412
ASC = "asc" # Ascending order
1413
DESC = "desc" # Descending order
1414
1415
class SupportedCurrencies(str, Enum):
1416
USD = "USD" # US Dollar (default)
1417
GBP = "GBP" # British Pound
1418
CHF = "CHF" # Swiss Franc
1419
EUR = "EUR" # Euro
1420
CAD = "CAD" # Canadian Dollar
1421
```
1422
1423
## Error Handling and Rate Limits
1424
1425
```python { .api }
1426
from alpaca.common.exceptions import APIError
1427
import time
1428
1429
def robust_data_fetch(client, request, max_retries=3):
1430
"""Fetch data with error handling and retries."""
1431
1432
for attempt in range(max_retries):
1433
try:
1434
return client.get_stock_bars(request)
1435
1436
except APIError as e:
1437
if e.status_code == 429: # Rate limit
1438
wait_time = 2 ** attempt # Exponential backoff
1439
print(f"Rate limited, waiting {wait_time}s (attempt {attempt + 1})")
1440
time.sleep(wait_time)
1441
continue
1442
elif e.status_code == 422: # Invalid parameters
1443
print(f"Invalid request parameters: {e.message}")
1444
break
1445
else:
1446
print(f"API error: {e.message} (status: {e.status_code})")
1447
if attempt == max_retries - 1:
1448
raise
1449
time.sleep(1)
1450
except Exception as e:
1451
print(f"Unexpected error: {e}")
1452
if attempt == max_retries - 1:
1453
raise
1454
time.sleep(1)
1455
1456
return None
1457
1458
# Usage
1459
bars = robust_data_fetch(data_client, bars_request)
1460
if bars:
1461
print(f"Successfully retrieved {len(bars.df)} bars")
1462
else:
1463
print("Failed to retrieve data after all retries")
1464
```
1465
1466
## Complete Market Data Analysis Example
1467
1468
```python { .api }
1469
import pandas as pd
1470
import numpy as np
1471
from datetime import datetime, timedelta
1472
from alpaca.data.historical import StockHistoricalDataClient
1473
from alpaca.data.requests import StockBarsRequest, StockLatestQuoteRequest
1474
from alpaca.data.timeframe import TimeFrame
1475
from alpaca.data.enums import Adjustment
1476
1477
class MarketAnalyzer:
1478
def __init__(self, api_key: str, secret_key: str):
1479
self.client = StockHistoricalDataClient(api_key, secret_key)
1480
1481
def get_historical_data(self, symbols: list, days: int = 30) -> pd.DataFrame:
1482
"""Get historical daily data for analysis."""
1483
1484
request = StockBarsRequest(
1485
symbol_or_symbols=symbols,
1486
timeframe=TimeFrame.Day,
1487
start=datetime.now() - timedelta(days=days),
1488
adjustment=Adjustment.ALL
1489
)
1490
1491
bars = self.client.get_stock_bars(request)
1492
df = bars.df.reset_index()
1493
1494
# Pivot for analysis
1495
pivot_df = df.pivot(index='timestamp', columns='symbol', values='close')
1496
return pivot_df.fillna(method='forward')
1497
1498
def calculate_metrics(self, df: pd.DataFrame) -> dict:
1499
"""Calculate key financial metrics."""
1500
1501
# Daily returns
1502
returns = df.pct_change().dropna()
1503
1504
# Metrics calculation
1505
metrics = {}
1506
1507
for symbol in df.columns:
1508
symbol_returns = returns[symbol].dropna()
1509
1510
metrics[symbol] = {
1511
'total_return': (df[symbol].iloc[-1] / df[symbol].iloc[0] - 1) * 100,
1512
'volatility': symbol_returns.std() * np.sqrt(252) * 100, # Annualized
1513
'sharpe_ratio': (symbol_returns.mean() / symbol_returns.std()) * np.sqrt(252),
1514
'max_drawdown': self.calculate_max_drawdown(df[symbol]),
1515
'current_price': df[symbol].iloc[-1]
1516
}
1517
1518
return metrics
1519
1520
def calculate_max_drawdown(self, price_series: pd.Series) -> float:
1521
"""Calculate maximum drawdown."""
1522
peak = price_series.expanding().max()
1523
drawdown = (price_series - peak) / peak
1524
return drawdown.min() * 100
1525
1526
def get_correlation_matrix(self, df: pd.DataFrame) -> pd.DataFrame:
1527
"""Calculate correlation matrix."""
1528
returns = df.pct_change().dropna()
1529
return returns.corr()
1530
1531
def screen_stocks(self, symbols: list) -> pd.DataFrame:
1532
"""Screen stocks based on various criteria."""
1533
1534
# Get current quotes
1535
quotes = self.client.get_stock_latest_quote(
1536
StockLatestQuoteRequest(symbol_or_symbols=symbols)
1537
)
1538
1539
# Get historical data for calculations
1540
df = self.get_historical_data(symbols, days=90)
1541
metrics = self.calculate_metrics(df)
1542
1543
# Build screening results
1544
screening_data = []
1545
1546
for symbol in symbols:
1547
if symbol in quotes and symbol in metrics:
1548
quote = quotes[symbol]
1549
metric = metrics[symbol]
1550
1551
screening_data.append({
1552
'symbol': symbol,
1553
'price': metric['current_price'],
1554
'bid': quote.bid_price,
1555
'ask': quote.ask_price,
1556
'spread_pct': ((quote.ask_price - quote.bid_price) / quote.ask_price) * 100,
1557
'total_return_pct': metric['total_return'],
1558
'volatility_pct': metric['volatility'],
1559
'sharpe_ratio': metric['sharpe_ratio'],
1560
'max_drawdown_pct': metric['max_drawdown']
1561
})
1562
1563
return pd.DataFrame(screening_data).set_index('symbol')
1564
1565
def generate_report(self, symbols: list) -> None:
1566
"""Generate comprehensive market report."""
1567
1568
print("š MARKET ANALYSIS REPORT")
1569
print("=" * 50)
1570
1571
# Get data and metrics
1572
df = self.get_historical_data(symbols, days=60)
1573
metrics = self.calculate_metrics(df)
1574
correlation = self.get_correlation_matrix(df)
1575
screening = self.screen_stocks(symbols)
1576
1577
# Performance summary
1578
print("\nš PERFORMANCE SUMMARY (60 Days)")
1579
print("-" * 30)
1580
1581
perf_df = pd.DataFrame({
1582
symbol: {
1583
'Return %': f"{metrics[symbol]['total_return']:.2f}%",
1584
'Volatility %': f"{metrics[symbol]['volatility']:.2f}%",
1585
'Sharpe': f"{metrics[symbol]['sharpe_ratio']:.2f}",
1586
'Max DD %': f"{metrics[symbol]['max_drawdown']:.2f}%"
1587
} for symbol in symbols if symbol in metrics
1588
}).T
1589
1590
print(perf_df)
1591
1592
# Top performers
1593
print("\nš TOP PERFORMERS")
1594
print("-" * 20)
1595
1596
sorted_by_return = screening.sort_values('total_return_pct', ascending=False)
1597
print(sorted_by_return[['price', 'total_return_pct', 'volatility_pct']].head())
1598
1599
# Risk analysis
1600
print("\nā ļø RISK ANALYSIS")
1601
print("-" * 15)
1602
1603
high_vol = screening[screening['volatility_pct'] > 30]
1604
if not high_vol.empty:
1605
print(f"High Volatility Stocks (>30%): {', '.join(high_vol.index)}")
1606
1607
wide_spreads = screening[screening['spread_pct'] > 0.5]
1608
if not wide_spreads.empty:
1609
print(f"Wide Spreads (>0.5%): {', '.join(wide_spreads.index)}")
1610
1611
# Correlation insights
1612
print("\nš CORRELATION INSIGHTS")
1613
print("-" * 22)
1614
1615
# Find highest correlations (excluding self-correlation)
1616
corr_matrix = correlation.where(np.triu(np.ones(correlation.shape), k=1).astype(bool))
1617
high_corr = corr_matrix.stack().sort_values(ascending=False).head(3)
1618
1619
print("Highest Correlations:")
1620
for (stock1, stock2), corr_val in high_corr.items():
1621
print(f" {stock1} - {stock2}: {corr_val:.3f}")
1622
1623
# Investment recommendations
1624
print("\nš” INVESTMENT INSIGHTS")
1625
print("-" * 21)
1626
1627
# Best risk-adjusted returns
1628
best_sharpe = screening.nlargest(3, 'sharpe_ratio')
1629
print("Best Risk-Adjusted Returns (Sharpe Ratio):")
1630
for symbol, data in best_sharpe.iterrows():
1631
print(f" {symbol}: {data['sharpe_ratio']:.2f}")
1632
1633
# Momentum stocks
1634
momentum = screening[(screening['total_return_pct'] > 10) & (screening['volatility_pct'] < 40)]
1635
if not momentum.empty:
1636
print(f"\nMomentum Candidates: {', '.join(momentum.index)}")
1637
1638
print("\n" + "=" * 50)
1639
print(f"Report generated at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
1640
1641
# Run comprehensive analysis
1642
if __name__ == "__main__":
1643
analyzer = MarketAnalyzer(
1644
api_key="your-api-key",
1645
secret_key="your-secret-key"
1646
)
1647
1648
# Analyze major tech stocks
1649
tech_stocks = ["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA", "META", "NVDA", "NFLX"]
1650
1651
analyzer.generate_report(tech_stocks)
1652
```