0
# Configuration and Utilities
1
2
Global configuration options, debugging utilities, and cache management for optimal performance and troubleshooting. These utilities help optimize yfinance behavior and diagnose issues in production environments.
3
4
## Capabilities
5
6
### Global Configuration
7
8
Configure global yfinance settings including proxy configuration and data source preferences.
9
10
```python { .api }
11
def set_config(proxy=None):
12
"""
13
Configure global yfinance settings.
14
15
Parameters:
16
- proxy: str or dict, proxy configuration for HTTP requests
17
Can be a string URL or dictionary with protocol-specific proxies
18
19
Examples:
20
- proxy="http://proxy.company.com:8080"
21
- proxy={"http": "http://proxy.com:8080", "https": "https://proxy.com:8080"}
22
"""
23
```
24
25
#### Usage Examples
26
27
```python
28
import yfinance as yf
29
30
# Configure HTTP proxy
31
yf.set_config(proxy="http://proxy.company.com:8080")
32
33
# Configure protocol-specific proxies
34
yf.set_config(proxy={
35
"http": "http://proxy.company.com:8080",
36
"https": "https://secure-proxy.company.com:8443"
37
})
38
39
# Configure SOCKS proxy
40
yf.set_config(proxy="socks5://proxy.company.com:1080")
41
42
# Remove proxy configuration
43
yf.set_config(proxy=None)
44
```
45
46
#### Configuration Best Practices
47
48
```python
49
# Corporate environment setup
50
def setup_corporate_environment():
51
"""Configure yfinance for corporate network environments."""
52
53
import os
54
55
# Check for corporate proxy environment variables
56
http_proxy = os.environ.get('HTTP_PROXY') or os.environ.get('http_proxy')
57
https_proxy = os.environ.get('HTTPS_PROXY') or os.environ.get('https_proxy')
58
59
if http_proxy or https_proxy:
60
proxy_config = {}
61
if http_proxy:
62
proxy_config['http'] = http_proxy
63
if https_proxy:
64
proxy_config['https'] = https_proxy
65
66
yf.set_config(proxy=proxy_config)
67
print(f"Configured proxy: {proxy_config}")
68
69
# Enable debug mode for troubleshooting
70
yf.enable_debug_mode()
71
72
# Usage
73
setup_corporate_environment()
74
```
75
76
### Debug Mode
77
78
Enable comprehensive debug logging for troubleshooting network issues, API responses, and data processing problems.
79
80
```python { .api }
81
def enable_debug_mode():
82
"""
83
Enable debug logging for yfinance operations.
84
85
This enables detailed logging of:
86
- HTTP requests and responses
87
- API endpoint calls
88
- Data processing steps
89
- Error conditions and stack traces
90
- Performance timing information
91
"""
92
```
93
94
#### Usage Examples
95
96
```python
97
import yfinance as yf
98
99
# Enable debug mode
100
yf.enable_debug_mode()
101
102
# Now all yfinance operations will produce detailed logging
103
ticker = yf.Ticker("AAPL")
104
data = ticker.history(period="1mo")
105
106
# Debug output will show:
107
# - URL being called
108
# - HTTP status codes
109
# - Response headers
110
# - Data parsing steps
111
# - Any errors encountered
112
```
113
114
#### Debug Mode Applications
115
116
```python
117
def troubleshoot_data_issues(symbol):
118
"""Troubleshoot data retrieval issues with debug logging."""
119
120
print(f"Troubleshooting data issues for {symbol}")
121
122
# Enable debug mode
123
yf.enable_debug_mode()
124
125
try:
126
ticker = yf.Ticker(symbol)
127
128
# Test different data sources
129
print("\n=== Testing Basic Info ===")
130
info = ticker.info
131
print(f"Info keys available: {len(info.keys()) if info else 0}")
132
133
print("\n=== Testing Historical Data ===")
134
history = ticker.history(period="5d")
135
print(f"History shape: {history.shape if not history.empty else 'Empty'}")
136
137
print("\n=== Testing Financial Data ===")
138
financials = ticker.income_stmt
139
print(f"Financials available: {not financials.empty}")
140
141
print("\n=== Testing Options Data ===")
142
options = ticker.options
143
print(f"Options expirations: {len(options) if options else 0}")
144
145
except Exception as e:
146
print(f"Error during troubleshooting: {e}")
147
import traceback
148
traceback.print_exc()
149
150
# Usage
151
troubleshoot_data_issues("AAPL")
152
```
153
154
### Cache Management
155
156
Configure and manage yfinance's internal caching system for improved performance and reduced API calls.
157
158
```python { .api }
159
def set_tz_cache_location(cache_dir: str):
160
"""
161
Set custom location for timezone cache data.
162
163
Parameters:
164
- cache_dir: str, directory path for storing cache files
165
Directory will be created if it doesn't exist
166
"""
167
```
168
169
#### Usage Examples
170
171
```python
172
import yfinance as yf
173
import os
174
175
# Set custom cache location
176
custom_cache_dir = os.path.expanduser("~/yfinance_cache")
177
yf.set_tz_cache_location(custom_cache_dir)
178
179
# Now timezone data will be cached in the custom directory
180
# This is useful for:
181
# - Persistent caching across application restarts
182
# - Shared cache in multi-user environments
183
# - Custom cache management policies
184
```
185
186
#### Advanced Cache Management
187
188
```python
189
import os
190
import shutil
191
from pathlib import Path
192
193
class YFinanceCacheManager:
194
"""Advanced cache management for yfinance."""
195
196
def __init__(self, base_cache_dir=None):
197
self.base_cache_dir = base_cache_dir or os.path.expanduser("~/.yfinance_cache")
198
self.setup_cache_structure()
199
200
def setup_cache_structure(self):
201
"""Create organized cache directory structure."""
202
cache_dirs = [
203
os.path.join(self.base_cache_dir, "timezone"),
204
os.path.join(self.base_cache_dir, "session_data"),
205
os.path.join(self.base_cache_dir, "temp")
206
]
207
208
for cache_dir in cache_dirs:
209
os.makedirs(cache_dir, exist_ok=True)
210
211
# Configure yfinance to use our timezone cache
212
tz_cache_dir = os.path.join(self.base_cache_dir, "timezone")
213
yf.set_tz_cache_location(tz_cache_dir)
214
215
def clear_cache(self, cache_type="all"):
216
"""Clear cache data."""
217
218
if cache_type == "all":
219
if os.path.exists(self.base_cache_dir):
220
shutil.rmtree(self.base_cache_dir)
221
self.setup_cache_structure()
222
print("All cache data cleared")
223
224
elif cache_type == "timezone":
225
tz_cache = os.path.join(self.base_cache_dir, "timezone")
226
if os.path.exists(tz_cache):
227
shutil.rmtree(tz_cache)
228
os.makedirs(tz_cache, exist_ok=True)
229
print("Timezone cache cleared")
230
231
def get_cache_size(self):
232
"""Get cache directory size in MB."""
233
234
total_size = 0
235
for dirpath, dirnames, filenames in os.walk(self.base_cache_dir):
236
for filename in filenames:
237
filepath = os.path.join(dirpath, filename)
238
total_size += os.path.getsize(filepath)
239
240
return total_size / (1024 * 1024) # Convert to MB
241
242
def cache_info(self):
243
"""Get cache information."""
244
245
info = {
246
'cache_dir': self.base_cache_dir,
247
'exists': os.path.exists(self.base_cache_dir),
248
'size_mb': self.get_cache_size() if os.path.exists(self.base_cache_dir) else 0
249
}
250
251
# Count files in each subdirectory
252
for subdir in ['timezone', 'session_data', 'temp']:
253
subdir_path = os.path.join(self.base_cache_dir, subdir)
254
if os.path.exists(subdir_path):
255
file_count = len([f for f in os.listdir(subdir_path)
256
if os.path.isfile(os.path.join(subdir_path, f))])
257
info[f'{subdir}_files'] = file_count
258
259
return info
260
261
# Usage
262
cache_manager = YFinanceCacheManager("/tmp/yfinance_cache")
263
264
# View cache information
265
cache_info = cache_manager.cache_info()
266
print(f"Cache location: {cache_info['cache_dir']}")
267
print(f"Cache size: {cache_info['size_mb']:.2f} MB")
268
269
# Clear specific cache type
270
cache_manager.clear_cache("timezone")
271
272
# Clear all cache
273
cache_manager.clear_cache("all")
274
```
275
276
## Performance Optimization
277
278
### Session Management
279
280
Optimize performance through proper session management and connection pooling.
281
282
```python
283
import requests
284
import yfinance as yf
285
from requests.adapters import HTTPAdapter
286
from requests.packages.urllib3.util.retry import Retry
287
288
def create_optimized_session():
289
"""Create an optimized requests session for yfinance."""
290
291
session = requests.Session()
292
293
# Configure retry strategy
294
retry_strategy = Retry(
295
total=3, # Total number of retries
296
backoff_factor=1, # Wait time between retries
297
status_forcelist=[429, 500, 502, 503, 504], # HTTP status codes to retry
298
method_whitelist=["HEAD", "GET", "OPTIONS"]
299
)
300
301
# Configure adapter with retry strategy
302
adapter = HTTPAdapter(
303
max_retries=retry_strategy,
304
pool_connections=20, # Number of connection pools
305
pool_maxsize=20, # Connections per pool
306
pool_block=False # Don't block when pool is full
307
)
308
309
# Mount adapter for both HTTP and HTTPS
310
session.mount("http://", adapter)
311
session.mount("https://", adapter)
312
313
# Set timeout
314
session.timeout = 30
315
316
# Set user agent
317
session.headers.update({
318
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
319
})
320
321
return session
322
323
# Usage with yfinance
324
optimized_session = create_optimized_session()
325
326
# Use session with Ticker objects
327
ticker = yf.Ticker("AAPL", session=optimized_session)
328
data = ticker.history(period="1mo")
329
330
# Use session with download function
331
bulk_data = yf.download(["AAPL", "GOOGL", "MSFT"],
332
period="1mo",
333
session=optimized_session)
334
```
335
336
### Batch Processing Utilities
337
338
Utilities for efficient batch processing of multiple tickers and operations.
339
340
```python
341
def batch_process_tickers(ticker_symbols, operations, batch_size=10,
342
session=None, error_handling='continue'):
343
"""
344
Process multiple tickers in batches with error handling.
345
346
Parameters:
347
- ticker_symbols: list, ticker symbols to process
348
- operations: list, operations to perform on each ticker
349
- batch_size: int, number of tickers to process simultaneously
350
- session: requests.Session, optional session for efficiency
351
- error_handling: str, 'continue', 'stop', or 'collect'
352
353
Returns:
354
dict with results and any errors encountered
355
"""
356
357
results = {}
358
errors = {}
359
360
# Process in batches
361
for i in range(0, len(ticker_symbols), batch_size):
362
batch = ticker_symbols[i:i + batch_size]
363
print(f"Processing batch {i//batch_size + 1}: {batch}")
364
365
for symbol in batch:
366
try:
367
ticker = yf.Ticker(symbol, session=session)
368
ticker_results = {}
369
370
# Perform requested operations
371
for operation in operations:
372
if operation == 'info':
373
ticker_results['info'] = ticker.info
374
elif operation == 'history':
375
ticker_results['history'] = ticker.history(period="1mo")
376
elif operation == 'financials':
377
ticker_results['financials'] = ticker.income_stmt
378
elif operation == 'recommendations':
379
ticker_results['recommendations'] = ticker.recommendations
380
# Add more operations as needed
381
382
results[symbol] = ticker_results
383
384
except Exception as e:
385
error_msg = f"Error processing {symbol}: {str(e)}"
386
errors[symbol] = error_msg
387
388
if error_handling == 'stop':
389
raise Exception(error_msg)
390
elif error_handling == 'continue':
391
print(f"Warning: {error_msg}")
392
# 'collect' mode just collects errors silently
393
394
return {
395
'results': results,
396
'errors': errors,
397
'success_count': len(results),
398
'error_count': len(errors)
399
}
400
401
# Usage
402
tickers = ["AAPL", "GOOGL", "MSFT", "AMZN", "TSLA"]
403
operations = ['info', 'history', 'recommendations']
404
405
batch_results = batch_process_tickers(
406
tickers,
407
operations,
408
batch_size=3,
409
session=create_optimized_session(),
410
error_handling='continue'
411
)
412
413
print(f"Successfully processed: {batch_results['success_count']} tickers")
414
print(f"Errors encountered: {batch_results['error_count']} tickers")
415
```
416
417
### Rate Limiting and Throttling
418
419
Implement rate limiting to avoid API restrictions and ensure reliable data access.
420
421
```python
422
import time
423
from functools import wraps
424
from collections import deque
425
from threading import Lock
426
427
class RateLimiter:
428
"""Rate limiter for yfinance API calls."""
429
430
def __init__(self, max_calls=100, time_window=60):
431
self.max_calls = max_calls
432
self.time_window = time_window
433
self.calls = deque()
434
self.lock = Lock()
435
436
def wait_if_needed(self):
437
"""Wait if rate limit would be exceeded."""
438
439
with self.lock:
440
now = time.time()
441
442
# Remove old calls outside time window
443
while self.calls and self.calls[0] <= now - self.time_window:
444
self.calls.popleft()
445
446
# Check if we need to wait
447
if len(self.calls) >= self.max_calls:
448
sleep_time = self.calls[0] + self.time_window - now
449
if sleep_time > 0:
450
print(f"Rate limit reached. Waiting {sleep_time:.2f} seconds...")
451
time.sleep(sleep_time)
452
# Clean up old calls after waiting
453
while self.calls and self.calls[0] <= time.time() - self.time_window:
454
self.calls.popleft()
455
456
# Record this call
457
self.calls.append(now)
458
459
def rate_limited_ticker_operation(rate_limiter):
460
"""Decorator to apply rate limiting to ticker operations."""
461
462
def decorator(func):
463
@wraps(func)
464
def wrapper(*args, **kwargs):
465
rate_limiter.wait_if_needed()
466
return func(*args, **kwargs)
467
return wrapper
468
return decorator
469
470
# Usage
471
api_limiter = RateLimiter(max_calls=50, time_window=60) # 50 calls per minute
472
473
@rate_limited_ticker_operation(api_limiter)
474
def get_ticker_data(symbol, data_type='history'):
475
"""Get ticker data with rate limiting."""
476
477
ticker = yf.Ticker(symbol)
478
479
if data_type == 'history':
480
return ticker.history(period="1mo")
481
elif data_type == 'info':
482
return ticker.info
483
elif data_type == 'financials':
484
return ticker.income_stmt
485
486
return None
487
488
# Process many tickers with automatic rate limiting
489
tickers = ["AAPL", "GOOGL", "MSFT", "AMZN", "TSLA", "META", "NVDA", "ORCL"]
490
results = {}
491
492
for symbol in tickers:
493
print(f"Processing {symbol}...")
494
results[symbol] = get_ticker_data(symbol, 'history')
495
print(f"Completed {symbol}")
496
497
print(f"Processed {len(results)} tickers with rate limiting")
498
```
499
500
## Logging and Monitoring
501
502
### Custom Logging Setup
503
504
Set up comprehensive logging for yfinance operations in production environments.
505
506
```python
507
import logging
508
import sys
509
from datetime import datetime
510
511
def setup_yfinance_logging(log_level=logging.INFO, log_file=None):
512
"""Set up comprehensive logging for yfinance operations."""
513
514
# Create logger
515
logger = logging.getLogger('yfinance_app')
516
logger.setLevel(log_level)
517
518
# Remove existing handlers
519
for handler in logger.handlers[:]:
520
logger.removeHandler(handler)
521
522
# Create formatter
523
formatter = logging.Formatter(
524
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
525
)
526
527
# Console handler
528
console_handler = logging.StreamHandler(sys.stdout)
529
console_handler.setLevel(log_level)
530
console_handler.setFormatter(formatter)
531
logger.addHandler(console_handler)
532
533
# File handler (optional)
534
if log_file:
535
file_handler = logging.FileHandler(log_file)
536
file_handler.setLevel(log_level)
537
file_handler.setFormatter(formatter)
538
logger.addHandler(file_handler)
539
540
return logger
541
542
# Usage
543
logger = setup_yfinance_logging(
544
log_level=logging.INFO,
545
log_file=f"yfinance_{datetime.now().strftime('%Y%m%d')}.log"
546
)
547
548
# Log yfinance operations
549
def logged_ticker_operation(symbol, operation):
550
"""Perform ticker operation with logging."""
551
552
logger.info(f"Starting {operation} for {symbol}")
553
start_time = time.time()
554
555
try:
556
ticker = yf.Ticker(symbol)
557
558
if operation == 'history':
559
result = ticker.history(period="1mo")
560
logger.info(f"Retrieved {len(result)} days of history for {symbol}")
561
elif operation == 'info':
562
result = ticker.info
563
logger.info(f"Retrieved info for {symbol}: {result.get('shortName', 'N/A')}")
564
565
elapsed_time = time.time() - start_time
566
logger.info(f"Completed {operation} for {symbol} in {elapsed_time:.2f}s")
567
568
return result
569
570
except Exception as e:
571
logger.error(f"Error in {operation} for {symbol}: {str(e)}")
572
raise
573
574
# Usage
575
data = logged_ticker_operation("AAPL", "history")
576
info = logged_ticker_operation("GOOGL", "info")
577
```
578
579
### Performance Monitoring
580
581
Monitor and optimize yfinance performance in production applications.
582
583
```python
584
import time
585
import psutil
586
import threading
587
from collections import defaultdict
588
589
class YFinancePerformanceMonitor:
590
"""Monitor performance metrics for yfinance operations."""
591
592
def __init__(self):
593
self.metrics = defaultdict(list)
594
self.active_operations = {}
595
self.lock = threading.Lock()
596
597
def start_operation(self, operation_name, symbol=None):
598
"""Start monitoring an operation."""
599
600
operation_id = f"{operation_name}_{symbol}_{int(time.time() * 1000)}"
601
602
with self.lock:
603
self.active_operations[operation_id] = {
604
'name': operation_name,
605
'symbol': symbol,
606
'start_time': time.time(),
607
'start_memory': psutil.Process().memory_info().rss / 1024 / 1024 # MB
608
}
609
610
return operation_id
611
612
def end_operation(self, operation_id, success=True, error_msg=None):
613
"""End monitoring an operation and record metrics."""
614
615
with self.lock:
616
if operation_id not in self.active_operations:
617
return
618
619
operation = self.active_operations.pop(operation_id)
620
621
end_time = time.time()
622
end_memory = psutil.Process().memory_info().rss / 1024 / 1024 # MB
623
624
metrics = {
625
'name': operation['name'],
626
'symbol': operation['symbol'],
627
'duration': end_time - operation['start_time'],
628
'memory_delta': end_memory - operation['start_memory'],
629
'success': success,
630
'error': error_msg,
631
'timestamp': end_time
632
}
633
634
self.metrics[operation['name']].append(metrics)
635
636
def get_performance_summary(self, operation_name=None):
637
"""Get performance summary for operations."""
638
639
if operation_name:
640
data = self.metrics.get(operation_name, [])
641
else:
642
data = []
643
for op_metrics in self.metrics.values():
644
data.extend(op_metrics)
645
646
if not data:
647
return {}
648
649
successful_ops = [m for m in data if m['success']]
650
failed_ops = [m for m in data if not m['success']]
651
652
if successful_ops:
653
durations = [m['duration'] for m in successful_ops]
654
memory_deltas = [m['memory_delta'] for m in successful_ops]
655
656
summary = {
657
'total_operations': len(data),
658
'successful_operations': len(successful_ops),
659
'failed_operations': len(failed_ops),
660
'success_rate': len(successful_ops) / len(data) * 100,
661
'avg_duration': sum(durations) / len(durations),
662
'max_duration': max(durations),
663
'min_duration': min(durations),
664
'avg_memory_delta': sum(memory_deltas) / len(memory_deltas),
665
'max_memory_delta': max(memory_deltas)
666
}
667
else:
668
summary = {
669
'total_operations': len(data),
670
'successful_operations': 0,
671
'failed_operations': len(failed_ops),
672
'success_rate': 0
673
}
674
675
return summary
676
677
# Usage
678
monitor = YFinancePerformanceMonitor()
679
680
def monitored_ticker_operation(symbol, operation):
681
"""Perform ticker operation with performance monitoring."""
682
683
op_id = monitor.start_operation(operation, symbol)
684
685
try:
686
ticker = yf.Ticker(symbol)
687
688
if operation == 'history':
689
result = ticker.history(period="1mo")
690
elif operation == 'info':
691
result = ticker.info
692
693
monitor.end_operation(op_id, success=True)
694
return result
695
696
except Exception as e:
697
monitor.end_operation(op_id, success=False, error_msg=str(e))
698
raise
699
700
# Run monitored operations
701
symbols = ["AAPL", "GOOGL", "MSFT"]
702
for symbol in symbols:
703
try:
704
data = monitored_ticker_operation(symbol, "history")
705
info = monitored_ticker_operation(symbol, "info")
706
except Exception as e:
707
print(f"Error processing {symbol}: {e}")
708
709
# Get performance summary
710
history_perf = monitor.get_performance_summary("history")
711
print(f"History operations: {history_perf['success_rate']:.1f}% success rate, "
712
f"avg duration: {history_perf['avg_duration']:.2f}s")
713
714
all_perf = monitor.get_performance_summary()
715
print(f"All operations: {all_perf['total_operations']} total, "
716
f"{all_perf['success_rate']:.1f}% success rate")
717
```
718
719
## Production Deployment Considerations
720
721
### Environment Configuration
722
723
```python
724
import os
725
from dataclasses import dataclass
726
from typing import Optional
727
728
@dataclass
729
class YFinanceConfig:
730
"""Configuration class for yfinance deployment."""
731
732
# Network settings
733
proxy_http: Optional[str] = None
734
proxy_https: Optional[str] = None
735
timeout: int = 30
736
max_retries: int = 3
737
738
# Cache settings
739
cache_dir: Optional[str] = None
740
enable_cache: bool = True
741
742
# Performance settings
743
max_concurrent_requests: int = 10
744
rate_limit_calls: int = 100
745
rate_limit_window: int = 60
746
747
# Logging settings
748
log_level: str = "INFO"
749
log_file: Optional[str] = None
750
enable_debug: bool = False
751
752
@classmethod
753
def from_environment(cls):
754
"""Create configuration from environment variables."""
755
756
return cls(
757
proxy_http=os.getenv('YFINANCE_PROXY_HTTP'),
758
proxy_https=os.getenv('YFINANCE_PROXY_HTTPS'),
759
timeout=int(os.getenv('YFINANCE_TIMEOUT', '30')),
760
max_retries=int(os.getenv('YFINANCE_MAX_RETRIES', '3')),
761
cache_dir=os.getenv('YFINANCE_CACHE_DIR'),
762
enable_cache=os.getenv('YFINANCE_ENABLE_CACHE', 'true').lower() == 'true',
763
max_concurrent_requests=int(os.getenv('YFINANCE_MAX_CONCURRENT', '10')),
764
rate_limit_calls=int(os.getenv('YFINANCE_RATE_LIMIT_CALLS', '100')),
765
rate_limit_window=int(os.getenv('YFINANCE_RATE_LIMIT_WINDOW', '60')),
766
log_level=os.getenv('YFINANCE_LOG_LEVEL', 'INFO'),
767
log_file=os.getenv('YFINANCE_LOG_FILE'),
768
enable_debug=os.getenv('YFINANCE_DEBUG', 'false').lower() == 'true'
769
)
770
771
def apply_configuration(self):
772
"""Apply configuration to yfinance."""
773
774
# Configure proxy
775
if self.proxy_http or self.proxy_https:
776
proxy_config = {}
777
if self.proxy_http:
778
proxy_config['http'] = self.proxy_http
779
if self.proxy_https:
780
proxy_config['https'] = self.proxy_https
781
yf.set_config(proxy=proxy_config)
782
783
# Configure cache
784
if self.enable_cache and self.cache_dir:
785
yf.set_tz_cache_location(self.cache_dir)
786
787
# Configure debug mode
788
if self.enable_debug:
789
yf.enable_debug_mode()
790
791
# Usage
792
config = YFinanceConfig.from_environment()
793
config.apply_configuration()
794
795
print(f"yfinance configured with:")
796
print(f" Cache: {config.cache_dir if config.enable_cache else 'Disabled'}")
797
print(f" Proxy: {config.proxy_http or config.proxy_https or 'None'}")
798
print(f" Debug: {config.enable_debug}")
799
```