A Python package for downloading historical and live data from TradingView. Supports up to 5000 bars of historical data on any supported timeframe, plus real-time data streaming.
NOTE: This is a fork of the original TvDatafeed project by StreamAlpha, with live data retrieving features and improved reliability.
- 📊 Download up to 5000 historical bars
- ⚡ Real-time data streaming with callbacks
- 🔒 Secure authentication (environment variables supported)
- 🌍 Support for stocks, crypto, forex, commodities
- 📈 13 timeframes (1m to 1M)
- 🧵 Thread-safe live feed implementation
- 🐼 Returns pandas DataFrames
- 🔍 Symbol search functionality
pip install --upgrade --no-cache-dir git+https://github.com/rongardF/tvdatafeed.gitgit clone https://github.com/rongardF/tvdatafeed.git
cd tvdatafeed
pip install -e .from tvDatafeed import TvDatafeed, Interval
# Initialize (no login required for limited access)
tv = TvDatafeed()
# Download historical data
df = tv.get_hist(
symbol='BTCUSDT',
exchange='BINANCE',
interval=Interval.in_1_hour,
n_bars=100
)
print(df.head())tv = TvDatafeed()Option 1: Environment Variables (Secure)
export TV_USERNAME="your_username"
export TV_PASSWORD="your_password"import os
tv = TvDatafeed(
username=os.getenv('TV_USERNAME'),
password=os.getenv('TV_PASSWORD')
)Option 2: Direct Credentials
tv = TvDatafeed(username='your_username', password='your_password')Option 3: Two-Factor Authentication (2FA)
✅ NEW: Authentication now uses HTTP requests that automatically bypass reCAPTCHA! No manual token extraction needed.
If you have 2FA enabled on your TradingView account, you can authenticate automatically:
Method A: TOTP Secret Key (Recommended)
The most convenient method is to use your TOTP secret key, which allows automatic code generation:
# In your .env file or shell
export TV_USERNAME="your_username"
export TV_PASSWORD="your_password"
export TV_TOTP_SECRET="JBSWY3DPEHPK3PXP" # Your TOTP secret keyimport os
from dotenv import load_dotenv
load_dotenv() # Load from .env file
tv = TvDatafeed(
username=os.getenv('TV_USERNAME'),
password=os.getenv('TV_PASSWORD'),
totp_secret=os.getenv('TV_TOTP_SECRET')
)How to get your TOTP secret key:
- Go to TradingView Settings > Security > Two-factor authentication
- When setting up 2FA, click "Can't scan?" to reveal the secret key
- The key looks like:
JBSWY3DPEHPK3PXP(base32 encoded)- Note: If 2FA is already set up, you may need to disable and re-enable it to see the secret
Method B: Manual 2FA Code
If you can't get the TOTP secret, you can provide the 6-digit code directly:
tv = TvDatafeed(
username='your_username',
password='your_password',
totp_code='123456' # Current 6-digit code from your authenticator app
)Note: Manual codes expire every 30 seconds, so this method requires you to update the code each time you connect.
from tvDatafeed import TvDatafeed, Interval
tv = TvDatafeed()
# Crypto
btc_data = tv.get_hist('BTCUSDT', 'BINANCE', Interval.in_1_hour, n_bars=100)
# Stocks
aapl_data = tv.get_hist('AAPL', 'NASDAQ', Interval.in_daily, n_bars=365)
# Futures continuous contract
nifty_futures = tv.get_hist('NIFTY', 'NSE', Interval.in_1_hour, n_bars=1000, fut_contract=1)
# Extended hours
extended_data = tv.get_hist('AAPL', 'NASDAQ', Interval.in_1_hour, n_bars=100, extended_session=True)NEW in v1.4: Instead of specifying n_bars, you can now fetch data by date range:
from datetime import datetime
from tvDatafeed import TvDatafeed, Interval
tv = TvDatafeed()
# Get data for January 2024
df = tv.get_hist(
'BTCUSDT',
'BINANCE',
Interval.in_1_hour,
start_date=datetime(2024, 1, 1),
end_date=datetime(2024, 1, 31)
)
# Get data for a specific week
df = tv.get_hist(
'AAPL',
'NASDAQ',
Interval.in_daily,
start_date=datetime(2024, 11, 1),
end_date=datetime(2024, 11, 7)
)
# Access timezone metadata
print(f"Timezone: {df.attrs.get('timezone', 'Not set')}")Notes:
start_dateandend_dateare mutually exclusive withn_bars- Both dates must be provided together
- Dates must be after 2000-01-01 and not in the future
- Timezone metadata is included in
df.attrs['timezone'] - Supports both timezone-aware and naive datetime objects
tv.get_hist(
symbol: str, # Symbol name
exchange: str, # Exchange name
interval: Interval, # Time interval
n_bars: int = None, # Number of bars (max 5000) - mutually exclusive with date range
fut_contract: int = None, # Futures contract (1=front, 2=next)
extended_session: bool = False, # Include extended hours
start_date: datetime = None, # Start date (use with end_date) - NEW in v1.4
end_date: datetime = None, # End date (use with start_date) - NEW in v1.4
timezone: str = None # Timezone for datetime index - NEW in v1.5
) -> pd.DataFrameBy default, timestamps are returned in your local system timezone. You can specify a timezone to get consistent datetime values:
# Get data in UTC (recommended for cross-instrument analysis)
df = tv.get_hist('BTCUSDT', 'BINANCE', Interval.in_1_hour, n_bars=100, timezone='UTC')
# Get data in US Eastern time
df = tv.get_hist('AAPL', 'NASDAQ', Interval.in_daily, n_bars=50, timezone='America/New_York')
# Common timezones:
# - 'UTC': Coordinated Universal Time
# - 'America/New_York': US Eastern (EST/EDT)
# - 'America/Chicago': US Central (CST/CDT)
# - 'Europe/London': UK (GMT/BST)
# - 'Europe/Paris': Central European (CET/CEST)
# - 'Asia/Tokyo': Japan Standard Time
# - 'Asia/Hong_Kong': Hong Kong TimeYou can also set the default timezone via environment variable:
export TV_TIMEZONE=UTCThe timezone is stored in DataFrame metadata:
print(df.attrs.get('timezone')) # Output: 'UTC'For real-time data monitoring with callbacks:
from tvDatafeed import TvDatafeedLive, Interval
import time
# Initialize (authentication required)
tvl = TvDatafeedLive(username='your_username', password='your_password')
# Create SEIS (Symbol-Exchange-Interval Set)
seis = tvl.new_seis('BTCUSDT', 'BINANCE', Interval.in_1_minute)
# Define callback
def on_new_data(seis, data):
print(f"New data: {data.iloc[-1]['close']}")
# Register callback
consumer = tvl.new_consumer(seis, on_new_data)
# Let it run
time.sleep(300) # 5 minutes
# Cleanup
tvl.del_consumer(consumer)
tvl.del_seis(seis)See examples/live_feed.py for detailed usage.
Find exact symbol names:
# Search for Bitcoin on Binance
results = tv.search_symbol('BTC', 'BINANCE')
for result in results:
print(f"{result['symbol']} - {result['description']}")
# Output:
# BTCUSDT - Bitcoin / TetherUS
# BTCUSD - Bitcoin / US Dollar
# ...Create a .env file (see .env.example for all options):
# Authentication
TV_USERNAME=your_username
TV_PASSWORD=your_password
# Network (optional)
TV_CONNECT_TIMEOUT=10.0
TV_RECV_TIMEOUT=30.0
TV_MAX_RETRIES=3
TV_WS_TIMEOUT=5.0 # WebSocket per-message timeout (seconds)
TV_MAX_RESPONSE_TIME=60.0 # Maximum time to wait for complete response (seconds)
# Debug
TV_DEBUG=false
# Logging
TV_VERBOSE=truefrom tvDatafeed.config import TvDatafeedConfig
# Load from environment variables
config = TvDatafeedConfig.from_env()
# Or use defaults
config = TvDatafeedConfig.default()Control log verbosity to reduce noise in production:
from tvDatafeed import TvDatafeed
# Quiet mode - only warnings and errors (production)
tv = TvDatafeed(verbose=False)
# Verbose mode - all info, warnings and errors (development/debugging)
tv = TvDatafeed(verbose=True) # Default# Set in .env file or shell
export TV_VERBOSE=falsefrom tvDatafeed import TvDatafeed
# Will automatically use TV_VERBOSE from environment
tv = TvDatafeed()When both parameter and environment variable are set, the parameter takes priority:
export TV_VERBOSE=true# This will use quiet mode despite TV_VERBOSE=true
tv = TvDatafeed(verbose=False)Verbose Mode (verbose=True) - Default behavior:
- ✅ Info messages (authentication, connection, data retrieval progress)
- ✅ Warnings (non-critical issues, fallbacks)
- ✅ Errors (critical failures)
- ✅ Debug messages (if logging.DEBUG is set)
Quiet Mode (verbose=False) - Production use:
- ❌ Info messages suppressed
- ✅ Warnings shown
- ✅ Errors always shown
- ❌ Debug messages suppressed
import logging
from tvDatafeed import TvDatafeed, Interval
# Configure root logger for production
logging.basicConfig(
level=logging.WARNING,
format='%(asctime)s - %(levelname)s - %(message)s'
)
# Create quiet TvDatafeed instance
tv = TvDatafeed(
username='your_username',
password='your_password',
verbose=False # Quiet mode
)
# Get data without noisy info logs
df = tv.get_hist('BTCUSDT', 'BINANCE', Interval.in_1_hour, n_bars=100)
# Only errors/warnings will be loggedUse verbose=True (default) when:
- Developing and debugging
- Learning the library
- Troubleshooting connection issues
- You want detailed progress information
Use verbose=False when:
- Running in production
- Scheduled/automated tasks
- Clean log output is important
- You only care about errors/warnings
Note: See examples/quiet_mode.py for a complete working example.
Check out the examples/ directory:
- basic_usage.py - Getting started guide
- live_feed.py - Real-time data monitoring
- error_handling.py - Robust error handling
- 2fa_authentication.py - 🆕 Two-Factor Authentication (TOTP)
- date_range_search.py - 🆕 Date Range Search (backtesting)
- quiet_mode.py - Verbose logging control
- captcha_workaround.py - Handle CAPTCHA requirement
- symbol_format_guide.py - Symbol formatting guide
Run examples:
python examples/basic_usage.pyInterval.in_1_minute # 1 minute
Interval.in_3_minute # 3 minutes
Interval.in_5_minute # 5 minutes
Interval.in_15_minute # 15 minutes
Interval.in_30_minute # 30 minutes
Interval.in_45_minute # 45 minutes
Interval.in_1_hour # 1 hour
Interval.in_2_hour # 2 hours
Interval.in_3_hour # 3 hours
Interval.in_4_hour # 4 hours
Interval.in_daily # 1 day
Interval.in_weekly # 1 week
Interval.in_monthly # 1 month- Crypto:
BINANCE,COINBASE,KRAKEN,BITFINEX - US Stocks:
NASDAQ,NYSE,AMEX - Indian Markets:
NSE,BSE - Commodities:
MCX,NYMEX,COMEX - Forex:
FX,OANDA
Understanding the correct symbol format is crucial for reliable data retrieval.
TradingView uses the format EXCHANGE:SYMBOL to uniquely identify instruments. While the library can work with just the symbol name, using the full format is more reliable.
# Crypto
df = tv.get_hist('BINANCE:BTCUSDT', 'BINANCE', Interval.in_1_hour)
# Stocks
df = tv.get_hist('NASDAQ:AAPL', 'NASDAQ', Interval.in_daily)
# Futures
df = tv.get_hist('NSE:NIFTY', 'NSE', Interval.in_15_minute, fut_contract=1)# Works but less reliable
df = tv.get_hist('BTCUSDT', 'BINANCE', Interval.in_1_hour)from tvDatafeed import TvDatafeed
tv = TvDatafeed()
# Search for Bitcoin on Binance
results = tv.search_symbol('BTC', 'BINANCE')
# Display formatted results
print(tv.format_search_results(results))
# Use the first result
if results:
exchange = results[0]['exchange']
symbol = results[0]['symbol']
full_symbol = f"{exchange}:{symbol}"
df = tv.get_hist(full_symbol, exchange, Interval.in_1_hour, n_bars=100)- Go to tradingview.com
- Use the search bar to find your instrument
- Look at the URL or chart title for the format
- Example: Bitcoin on Binance shows as
BINANCE:BTCUSDT
Cryptocurrency:
'BINANCE:BTCUSDT' # Bitcoin/Tether on Binance
'COINBASE:BTCUSD' # Bitcoin/USD on Coinbase
'BINANCE:ETHUSDT' # Ethereum/Tether on Binance
'KRAKEN:BTCEUR' # Bitcoin/Euro on KrakenUS Stocks:
'NASDAQ:AAPL' # Apple Inc.
'NYSE:TSLA' # Tesla Inc.
'NASDAQ:GOOGL' # Alphabet Inc.
'NYSE:JPM' # JPMorgan ChaseIndian Markets:
'NSE:NIFTY' # Nifty 50 Index
'NSE:RELIANCE' # Reliance Industries
'BSE:SENSEX' # Sensex IndexForex:
'FX:EURUSD' # Euro/US Dollar
'OANDA:GBPJPY' # British Pound/Japanese YenCommodities:
'MCX:CRUDEOIL' # Crude Oil on MCX
'NYMEX:CL1!' # WTI Crude Oil Futures
'COMEX:GC1!' # Gold FuturesIf you're experiencing timeout errors, you can increase the WebSocket timeout:
from tvDatafeed import TvDatafeed
# Increase timeout to 30 seconds
tv = TvDatafeed(ws_timeout=30.0)
df = tv.get_hist('BINANCE:BTCUSDT', 'BINANCE', Interval.in_1_hour)# Set in shell or .env file
export TV_WS_TIMEOUT=30.0# Will automatically use TV_WS_TIMEOUT
tv = TvDatafeed()from tvDatafeed import TvDatafeed
from tvDatafeed.config import NetworkConfig
config = NetworkConfig(recv_timeout=30.0)
# Note: Currently NetworkConfig is read from environment
# Use TV_RECV_TIMEOUT environment variable insteadexport TV_RECV_TIMEOUT=30.0- Always use EXCHANGE:SYMBOL format when possible
- Use search_symbol() to find correct symbol names
- Test with small n_bars first (e.g., n_bars=10)
- Increase timeout for slow connections or large data requests
- Enable debug logging to see what's happening:
import logging
logging.basicConfig(level=logging.DEBUG)
tv = TvDatafeed()
tv.ws_debug = True # See WebSocket messagesPossible causes:
- Symbol name incorrect → Use
search_symbol()to find exact name - Symbol not available on specified exchange
- Authentication required for this symbol
Solution:
# Find correct symbol name
results = tv.search_symbol('BTC', 'BINANCE')
print(results[0]['symbol']) # Use this symbolPossible causes:
- Incorrect username/password
- 2FA enabled → Use
totp_secretortotp_codeparameter (see 2FA Authentication) - Credentials not in environment variables
- CAPTCHA required (see below)
Solution:
export TV_USERNAME="your_username"
export TV_PASSWORD="your_password"✅ RESOLVED IN v2.0: The authentication method now uses HTTP requests that automatically bypass reCAPTCHA detection! Simply use
username/password(+totp_secretif 2FA enabled) - no manual token extraction needed. The JWT token method below is now optional for advanced use cases.
Legacy Issue (Pre-v2.0): When trying to authenticate with username/password, you may have seen this error:
AuthenticationError: Authentication failed: You have been locked out. Please try again later.
This is NOT a real rate limit - it's TradingView's invisible reCAPTCHA blocking automated logins.
TradingView uses Google reCAPTCHA v2 invisible on their login page. This reCAPTCHA:
- Runs automatically in the browser via JavaScript
- Validates that you're human before allowing login
- Cannot be bypassed by Python scripts (no JavaScript execution)
When reCAPTCHA validation fails (which it always does from scripts), TradingView returns a generic "rate_limit" error instead of a clear "reCAPTCHA required" message.
Instead of username/password, extract your JWT auth token from the browser and use it directly.
- Log in to https://www.tradingview.com in your browser
- Open DevTools (F12 or Ctrl+Shift+I)
- Go to Console tab
- Type this command:
window.user.auth_token
- Copy the token - it looks like this:
eyJhbGciOiJSUzUxMiIsImtpZCI6IkdaeFUiLCJ0eXAiOiJKV1QifQ.eyJ1c2VyX2lkIjo...
Method A: Environment Variable (Recommended)
# In your .env file or shell
export TV_AUTH_TOKEN="eyJhbGciOiJSUzUxMiIs..."import os
from tvDatafeed import TvDatafeed
tv = TvDatafeed(auth_token=os.getenv('TV_AUTH_TOKEN'))
# Now you can fetch data with full Pro/Premium access
df = tv.get_hist('BTCUSDT', 'BINANCE', Interval.in_1_hour, n_bars=5000)Method B: Direct Usage (Testing Only)
from tvDatafeed import TvDatafeed
tv = TvDatafeed(auth_token='eyJhbGciOiJSUzUxMiIs...')If window.user.auth_token doesn't work:
- Open DevTools > Network tab
- Filter by "WS" (WebSocket)
- Refresh the page or open a chart
- Click on the WebSocket connection to
wss://data.tradingview.com - Go to "Messages" tab
- Look for
set_auth_tokenmessage - copy the token value
| Aspect | Details |
|---|---|
| Token Type | JWT (JSON Web Token) - starts with eyJ |
| NOT the sessionid | The sessionid cookie does NOT work for WebSocket auth |
| Expiration | Tokens expire (usually 4-24 hours). Extract a new one when needed |
| Security | Treat like a password - never commit to git |
| Plan Features | Token includes your subscription (Pro, Premium, etc.) |
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Your Script │────▶│ TradingView │────▶│ reCAPTCHA │
│ (Python/HTTP) │ │ Login Page │ │ (Invisible) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ JavaScript │
│ Validation │
│ (Not in Python)│
└─────────────────┘
│
▼
┌─────────────────┐
│ ❌ FAILS │
│ "rate_limit" │
└─────────────────┘
The script cannot execute JavaScript, so reCAPTCHA always fails, returning a misleading "rate_limit" error.
Note: This section is kept for backward compatibility. The reCAPTCHA solution above is the recommended approach.
If you see CaptchaRequiredError, follow the JWT token extraction method above.
See examples/captcha_workaround.py for a complete working example with error handling:
python examples/captcha_workaround.py- ⏱️ Token Expiration: Tokens expire after some time. Extract a new one if needed.
- 🔒 Security: Treat your token like a password. Never commit it to version control.
- 🔄 Session-tied: Token is tied to your TradingView session.
- 📝 Storage: Store in environment variables or secure configuration files.
For automated/scheduled scripts running on servers, you can use the token management utilities:
pip install playwright playwright-stealth pyotp
playwright install chromium- Configure your
.envfile:
TV_USERNAME=your_username
TV_PASSWORD=your_password
TV_TOTP_SECRET=your_totp_secret # Optional, for 2FA accounts- Extract token (first time or when expired):
python scripts/get_auth_token.pyfrom scripts.token_manager import get_valid_token, get_token_info
from tvDatafeed import TvDatafeed, Interval
# Get valid token (auto-refreshes if expired)
token = get_valid_token(auto_refresh=True)
if token:
# Check token info
info = get_token_info(token)
print(f"Plan: {info['plan']}, Expires: {info['expires_at']}")
# Use with TvDatafeed
tv = TvDatafeed(auth_token=token)
df = tv.get_hist('BTCUSDT', 'BINANCE', Interval.in_1_hour, 100)- Priority: Environment variable > Cache file > Auto-refresh
- Validation: Checks expiration with configurable threshold (default: 1 hour before expiry)
- Caching: Stores token in
.token_cache.jsonwith secure permissions - Auto-refresh: Uses Playwright + stealth mode to get fresh token
See examples/automated_data_fetch.py for a complete server script example.
Possible causes:
- Slow internet connection
- TradingView servers busy
Solution:
# Increase timeout (requires config module)
from tvDatafeed.config import NetworkConfig
config = NetworkConfig(recv_timeout=60.0)import logging
logging.basicConfig(level=logging.DEBUG)
tv = TvDatafeed()
tv.ws_debug = TrueRun tests:
# Install dev dependencies
pip install -r requirements-dev.txt
# Run all tests
pytest
# Run with coverage
pytest --cov=tvDatafeed --cov-report=html
# Run specific tests
pytest tests/unit/test_seis.pyTvDatafeed returns pandas DataFrames, making it easy to use with TA-Lib:
import talib
df = tv.get_hist('BTCUSDT', 'BINANCE', Interval.in_1_hour, n_bars=100)
# Calculate indicators
df['SMA_20'] = talib.SMA(df['close'], timeperiod=20)
df['RSI'] = talib.RSI(df['close'], timeperiod=14)
df['MACD'], df['MACD_signal'], df['MACD_hist'] = talib.MACD(df['close'])Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
Quick start:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests
- Run tests (
pytest) - Commit (
git commit -m 'feat: add amazing feature') - Push (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Original project by StreamAlpha
- TradingView for providing the data
- All contributors
This is an unofficial library. Use at your own risk. Always verify data accuracy before making trading decisions.
v1.2 Tutorial with Backtrader Integration
Full Tutorial
Before creating an issue:
- Search existing issues - Your problem might already be reported
- Use latest version - Update to the latest version
- Provide details:
- Python version
- Operating system
- Full error traceback
- Minimal code to reproduce
- Expected vs actual behavior
See CONTRIBUTING.md for issue templates.
Support the original author:


