You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

13 KiB

yfinance-plus 🚀

A drop-in replacement for yfinance with improved rate limiting, intelligent caching, and 401 error handling. Works exactly like the original yfinance but faster, more reliable, and with zero breaking changes.

Python 3.8+ License: MIT

Key Features

🎯 100% Drop-in Replacement

  • Import as import yfinance_plus as yf
  • All yfinance functionality works identically
  • 99% API coverage (97/98 features supported)
  • Zero code changes required

🛡️ Enhanced Rate Limiting & Error Handling

  • Automatic 401 error handling with session refresh
  • Browser-like headers with 7 different User-Agent strings
  • Exponential backoff with intelligent retry logic
  • Session rotation every 10 minutes or 50 requests

Intelligent Caching System

  • 247x faster repeated requests with file-based caching
  • Smart period matching - longer periods serve shorter requests
  • Automatic cache invalidation after 1 hour
  • CLI cache management with detailed file information

🔧 Enhanced CLI Tools

  • yfp command for cache and configuration management
  • Detailed cache inspection with symbol, period, and date range info
  • Performance benchmarking and connection testing
  • Configuration management with easy-to-use commands

📦 Installation

# Clone and install
git clone https://gitea.yirugi.synology.me/yirugi/yfinance_plus.git
cd yfinance-plus
pip install -e .

Method 2: Local Development

# Install dependencies
pip install yfinance>=0.2.65 curl_cffi>=0.5.0 pandas>=1.3.0 numpy>=1.20.0

🚀 Quick Start

Drop-in Replacement Usage

# Simply replace your import - everything else stays the same!
import yfinance_plus as yf

# All original yfinance functionality works identically
ticker = yf.Ticker("AAPL")
hist = ticker.history(period="1mo")
info = ticker.info  # Note: property, not method
data = yf.download(["AAPL", "GOOGL"], period="1mo")

# Multiple tickers
tickers = yf.Tickers("MSFT AAPL GOOG")
msft_info = tickers.tickers['MSFT'].info

# New cache management features
yf.clear_cache()
cache_info = yf.get_cache_info()

Performance Comparison

import time
import yfinance_plus as yf

# First request (downloads and caches)
start = time.time()
data1 = yf.Ticker("AAPL").history(period="1mo")
first_time = time.time() - start
print(f"First request: {first_time:.3f}s")

# Second request (from cache)
start = time.time()
data2 = yf.Ticker("AAPL").history(period="1mo")
second_time = time.time() - start
print(f"Second request: {second_time:.3f}s")
print(f"Speedup: {first_time/second_time:.1f}x")

# Output:
# First request: 0.186s
# Second request: 0.001s  
# Speedup: 247.9x

🔧 Command Line Interface

Available Commands

yfp --help                 # Show all available commands
yfp cache info             # Show detailed cache information  
yfp cache info --limit 5   # Show only 5 most recent files
yfp cache clear            # Clear cache with confirmation
yfp cache clear --force    # Clear cache without confirmation
yfp config show           # Show current configuration
yfp test                   # Test connection to Yahoo Finance
yfp benchmark              # Run performance benchmark

Enhanced Cache Information

$ yfp cache info
🗂️  YFinance Plus Cache Information
==================================================
Cache Directory: /Users/username/.yfinance_cache
Number of Files: 8
Total Size: 30.5 KB

📁 Cache Files:
----------------------------------------------------------------------------------------------------
  📄 06dbc103b9a8af2faddce6c95b2caaef   5.4 KB 2025-08-01 21:59:40
      Symbol: NVDA     Period: 3mo    Interval: 1d   Rows: 63
      Date Range: 2025-05-02 to 2025-08-01

  📄 0d84bd5865d16d346aabcaed64cf02f0   2.8 KB 2025-08-01 21:59:40
      Symbol: NVDA     Period: 1mo    Interval: 1d   Rows: 22
      Date Range: 2025-07-02 to 2025-08-01

Configuration Management

$ yfp config show
⚙️  YFinance Plus Configuration
========================================
Max Retries: 3
Base Delay: 1.0s
Max Delay: 60.0s
Jitter Enabled: True
Cache Enabled: True
Cache Directory: /Users/username/.yfinance_cache
User Agents: 7 configured

Connection Testing

$ yfp test
🔗 Testing connection to Yahoo Finance...
  Creating test ticker...
  Fetching basic info...
  Fetching historical data...
✅ Connection test successful!
   Company: Apple Inc.
   Historical data points: 5
   Cache status: 8 files

💾 Intelligent Caching System

How Smart Caching Works

import yfinance_plus as yf

# 1. First request caches data
ticker = yf.Ticker("AAPL")
data_1mo = ticker.history(period="1mo")  # Downloads and caches

# 2. Second identical request uses cache
data_1mo_again = ticker.history(period="1mo")  # Instant from cache

# 3. Longer period downloads new data
data_3mo = ticker.history(period="3mo")  # Downloads 3mo, caches it

# 4. Shorter request extracts from longer cached data
data_1mo_from_3mo = ticker.history(period="1mo")  # Extracted from 3mo cache

What Gets Cached

Cached:

  • Historical price data (history())
  • All standard periods (1d, 1mo, 3mo, 1y, etc.)
  • All intervals (1m, 5m, 1h, 1d)
  • Smart overlap detection - longer periods serve shorter requests

Not Cached:

  • Real-time data
  • News articles (news)
  • Extended hours data (prepost=True)
  • Data older than 1 hour (auto-expires)

Cache Management

import yfinance_plus as yf

# Check cache status
cache_info = yf.get_cache_info()
print(f"Cache files: {cache_info['file_count']}")
print(f"Cache size: {cache_info['total_size_mb']:.2f} MB")

# Clear cache
yf.clear_cache()

# Configure caching
yf.set_config(
    enable_cache=True,
    cache_dir="~/my_custom_cache"
)

⚙️ Configuration

Global Configuration

import yfinance_plus as yf

# Configure global settings
yf.set_config(
    max_retries=5,           # Number of retry attempts
    base_delay=1.0,          # Base delay between retries (seconds)
    max_delay=60.0,          # Maximum delay (seconds)
    jitter=True,             # Add randomness to delays
    enable_cache=True,       # Enable intelligent caching
    cache_dir="~/my_cache"   # Custom cache directory
)

# Get current configuration
config = yf.get_config()
print(f"Max retries: {config.max_retries}")

Configuration Parameters

Parameter Default Description
max_retries 3 Maximum retry attempts for failed requests
base_delay 1.0 Base delay between retries (seconds)
max_delay 60.0 Maximum delay between retries (seconds)
jitter True Add random jitter to delays
enable_cache True Enable intelligent caching system
cache_dir ~/.yfinance_cache Cache directory location
user_agents 7 agents List of User-Agent strings to rotate

🔄 Migration Guide

From yfinance to yfinance-plus

Migration is 100% seamless - just change your import:

# Before
import yfinance as yf

# After
import yfinance_plus as yf

# Everything else works identically!
ticker = yf.Ticker("AAPL")
data = yf.download(["AAPL", "GOOGL"], period="1mo")
tickers = yf.Tickers("MSFT AAPL GOOG")
info = tickers.tickers['MSFT'].info

Common yfinance Patterns That Work

import yfinance_plus as yf

# All standard patterns work identically
ticker = yf.Ticker("AAPL")

# Properties (not methods!)
info = ticker.info                    # ✅ Correct
dividends = ticker.dividends          # ✅ Correct  
splits = ticker.splits                # ✅ Correct
financials = ticker.financials        # ✅ Correct
balance_sheet = ticker.balance_sheet  # ✅ Correct
cashflow = ticker.cashflow            # ✅ Correct

# Methods
history = ticker.history(period="1mo")        # ✅ Correct
options = ticker.options                      # ✅ Correct
option_chain = ticker.option_chain("2024-01-19")  # ✅ Correct

# Bulk operations
data = yf.download(["AAPL", "GOOGL", "MSFT"], 
                   period="1y", interval="1d")

# Multiple tickers
tickers = yf.Tickers("AAPL GOOGL MSFT")
for symbol in tickers.symbols:
    hist = tickers.tickers[symbol].history(period="1mo")

🚀 Enhanced Features

401 Error Handling

# Automatic 401 error recovery
import yfinance_plus as yf

ticker = yf.Ticker("AAPL")
# If Yahoo returns 401, automatically:
# 1. Creates new session with fresh headers
# 2. Retries with exponential backoff  
# 3. Rotates User-Agent strings
# 4. Works silently in background
info = ticker.info  # Just works!

Advanced Rate Limiting

# The wrapper automatically:
# - Detects rate limit errors (429, 401)
# - Refreshes sessions every 10 minutes
# - Uses 7 different User-Agent strings
# - Adds realistic browser cookies
# - Implements exponential backoff
# - No user intervention required

Performance Optimization

import yfinance_plus as yf

# Smart cache usage
ticker = yf.Ticker("AAPL") 

# Downloads 1 year of data
hist_1y = ticker.history(period="1y")  # ~0.5s, cached

# Uses cached data for shorter periods  
hist_6mo = ticker.history(period="6mo")  # ~0.001s, from cache
hist_3mo = ticker.history(period="3mo")  # ~0.001s, from cache
hist_1mo = ticker.history(period="1mo")  # ~0.001s, from cache

# Result: 4 requests in ~0.5s instead of ~2.0s

📊 API Compatibility

100% Compatible Functions

Function yfinance yfinance-plus Enhancements
Ticker(symbol) + Caching + Rate limiting
download(tickers) + Better error handling
Tickers(symbols) + Enhanced performance
ticker.history() + Intelligent caching
ticker.info + 401 error handling
ticker.financials + Automatic retries
ticker.dividends + Session management
ticker.splits + Rate limiting
All other methods + Enhanced reliability

New Enhanced Functions

Function Description
yf.clear_cache() Clear all cached data
yf.get_cache_info() Get detailed cache statistics
yf.set_config(**kwargs) Configure global settings
yf.get_config() Get current configuration

🔍 Technical Details

Browser Emulation

# Automatically rotates between realistic headers:
user_agents = [
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2.1 Safari/605.1.15',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
    # ... 3 more variants
]

# Plus realistic browser cookies:
cookies = {
    'A1': 'd=AQABBC123456YLoCIgEBwQJ7vgAB&S=AQAAAg',
    'GUC': 'AQEBAQFm1234k0L',
    'B': 'c=1234567&b=3&s=4u',
    # ... more realistic cookies
}

Intelligent Cache Strategy

# Cache key generation
cache_key = hash(f"{symbol}_{period}_{interval}_{start}_{end}")

# Smart period matching  
if requested_period <= cached_period:
    return cached_data.tail(requested_rows)  # Extract from cache
else:
    fetch_new_data()  # Download longer period

Error Recovery

# Automatic error handling
if "401" in error or "unauthorized" in error:
    refresh_session()      # New headers and cookies
    exponential_backoff()  # Wait before retry
    retry_request()        # Try again

🧪 Testing

Running Tests

# Run comprehensive tests
python test_drop_in_replacement.py
python test_comprehensive_wrapping.py

# CLI tests
yfp test
yfp benchmark
yfp cache info

Test Results

🚀 Comprehensive yfinance Enhanced Wrapping Test
======================================================================
📊 Functionality coverage: 97/98 (99.0%)
✅ EXCELLENT: Nearly all yfinance functionality is working!

Cache speedup: 247.9x
Bulk download speedup: 4.36x

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📝 License

This project is licensed under the MIT License - see the LICENSE file for details.

⚠️ Disclaimer

This project is for educational and research purposes. Please respect Yahoo Finance's terms of service and rate limits. The yfinance-plus wrapper is designed to be more respectful of their servers while providing better reliability for legitimate use cases.

🙏 Acknowledgments

  • Built on top of the excellent yfinance library
  • Uses curl_cffi for advanced browser emulation
  • Inspired by the need for more reliable financial data access

Made with ❤️ for the Python financial data community