This is a data source library for algorithmic trading written in Rust inspired by TradingView-API. It provides programmatic access to TradingView's data and features through a robust, async-first API.
The library exposes two usage tiers:
- High-level — An event-driven DataLoader that connects a source to multiple sinks with backpressure and graceful shutdown.
- Low-level — Direct access to HTTP clients, WebSocket sessions, and raw message parsing for full control.
⚠️ Alpha Stage: This library is currently in alpha stage and not ready for production use. Breaking changes may occur between versions.
DataSource → fan-out → EventSink architecture with bounded channels, cancellation tokens, and error recoveryEventSink traitAdd this to your Cargo.toml:
[dependencies]
# From crates.io (recommended):
tradingview-rs = "0.1"
# Or from the Git repository:
tradingview-rs = { git = "https://github.com/bitbytelabio/tradingview-rs.git", branch = "main" }
| Feature | Default | Description |
|---|---|---|
rustls-tls |
✅ | TLS via rustls (recommended) |
native-tls |
— | TLS via platform-native libraries |
user |
✅ | User authentication (login, 2FA, session cookies) |
Example with optional features:
[dependencies]
tradingview-rs = { version = "0.1", default-features = false, features = ["native-tls", "user"] }
use tradingview::{DataServer, Interval, history};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let auth_token = std::env::var("TV_AUTH_TOKEN").expect("TV_AUTH_TOKEN is not set");
let (_info, data) = history::single::retrieve()
.auth_token(&auth_token)
.symbol("BTCUSDT")
.exchange("BINANCE")
.interval(Interval::OneHour)
.with_replay(true)
.server(DataServer::ProData)
.call()
.await?;
println!("Retrieved {} data points", data.len());
Ok(())
}
use tradingview::{Interval, Symbol, history};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let auth_token = std::env::var("TV_AUTH_TOKEN").expect("TV_AUTH_TOKEN is not set");
let symbols = vec![
Symbol::builder().symbol("BTCUSDT").exchange("BINANCE").build(),
Symbol::builder().symbol("ETHUSDT").exchange("BINANCE").build(),
];
let datamap = history::batch::retrieve()
.auth_token(&auth_token)
.symbols(&symbols)
.interval(Interval::OneHour)
.call()
.await?;
for (symbol_info, ticker_data) in datamap.values() {
println!("{}: {} data points", symbol_info.name, ticker_data.len());
}
Ok(())
}
use tradingview::{list_symbols, prelude::*};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let symbols = list_symbols()
.market_type(MarketType::All)
.call()
.await?;
println!("Found {} symbols", symbols.len());
Ok(())
}
use tradingview::UserCookies;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let username = std::env::var("TV_USERNAME").expect("TV_USERNAME is not set");
let password = std::env::var("TV_PASSWORD").expect("TV_PASSWORD is not set");
let totp = std::env::var("TV_TOTP_SECRET").expect("TV_TOTP_SECRET is not set");
let user = UserCookies::default()
.login(&username, &password, Some(&totp))
.await?;
// Save cookies for later use
let json = serde_json::to_string_pretty(&user)?;
std::fs::write("tv_user_cookies.json", json)?;
Ok(())
}
use dotenv::dotenv;
use std::{env, sync::Arc, time::Duration};
use tokio::{sync::mpsc, time::sleep};
use tradingview::{
ChartOptions, Interval,
live::{
handler::{
command::CommandRunner,
message::{Command, TradingViewResponse},
},
models::DataServer,
websocket::WebSocketClient,
},
};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
dotenv().ok();
let auth_token = env::var("TV_AUTH_TOKEN").expect("TV_AUTH_TOKEN is not set");
// Create communication channels
let (response_tx, mut response_rx) = mpsc::unbounded_channel();
let (command_tx, command_rx) = mpsc::unbounded_channel();
// Create WebSocket client
let ws_client = WebSocketClient::builder()
.auth_token(&auth_token)
.server(DataServer::ProData)
.data_tx(response_tx)
.build()
.await?;
// Create command runner
let command_runner = CommandRunner::new(command_rx, Arc::clone(&ws_client));
// Spawn command runner
tokio::spawn(async move {
command_runner.run().await.unwrap();
});
// Handle responses
tokio::spawn(async move {
while let Some(response) = response_rx.recv().await {
match response {
TradingViewResponse::ChartData(series_info, data_points) => {
println!("Chart Data: {} points", data_points.len());
}
TradingViewResponse::QuoteData(quote) => {
println!("Quote: {:?}", quote);
}
_ => {}
}
}
});
// Set up market data
let options = ChartOptions::builder()
.symbol("BTCUSDT".into())
.exchange("BINANCE".into())
.interval(Interval::OneMinute)
.build();
command_tx.send(Command::set_market(options))?;
command_tx.send(Command::add_symbol("NASDAQ:AAPL"))?;
// Keep running
sleep(Duration::from_secs(60)).await;
Ok(())
}
use tradingview::{
ChartOptions, Interval, StudyOptions,
get_builtin_indicators,
pine_indicator::{BuiltinIndicators, ScriptType},
};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Get built-in indicators
let indicators = get_builtin_indicators(BuiltinIndicators::Standard).await?;
if let Some(indicator) = indicators.first() {
let opts = ChartOptions::builder()
.symbol("BTCUSDT".into())
.exchange("BINANCE".into())
.interval(Interval::OneDay)
.bar_count(20)
.study_config(StudyOptions {
script_id: (&indicator.script_id).into(),
script_version: (&indicator.script_version).into(),
script_type: ScriptType::IntervalScript,
})
.build();
// Use opts with WebSocket client for real-time indicator data
}
Ok(())
}
The examples/ directory contains runnable examples for every major feature:
| Example | Description |
|---|---|
historical_data_fetch.rs |
Fetch historical OHLCV for a single symbol |
batch_historical_fetch.rs |
Concurrent batch historical data |
live_quote.rs |
Real-time quote streaming via WebSocket |
channel_consumer.rs |
Event-driven loader with a channel sink |
callback_consumer.rs |
Event-driven loader with an inline callback sink |
user.rs |
User authentication and session management |
search.rs |
Symbol search and filtering |
misc.rs |
Miscellaneous utility functions |
Run an example:
cargo run --example historical_data_fetch
cargo run --example live_quote
cargo run --example channel_consumer
For examples requiring authentication, create a .env file:
TV_USERNAME=your_username
TV_PASSWORD=your_password
TV_TOTP_SECRET=your_2fa_secret # Optional, for 2FA
TV_AUTH_TOKEN=your_auth_token # Get from user authentication
┌──────────────────────────────────────────────────┐
│ tradingview-rs │
├──────────────────────────────────────────────────┤
│ High-Level API (event-driven) │
│ ┌──────────┐ ┌───────────┐ ┌────────────┐ │
│ │ Source │───▶│ DataLoader │───▶│ EventSink │ │
│ │ (TV feed) │ │ (fan-out) │ │ (channel, │ │
│ │ │ │ │ │ callback, │ │
│ │ │ │ │ │ kafka) │ │
│ └──────────┘ └───────────┘ └────────────┘ │
├──────────────────────────────────────────────────┤
│ Low-Level API (direct access) │
│ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ │
│ │ historical │ │ live │ │ client │ │
│ │ (WebSocket) │ │ (WebSocket) │ │ (REST) │ │
│ └──────────────┘ └──────────────┘ └───────────┘ │
├──────────────────────────────────────────────────┤
│ Shared: models, chart, quote, error, utils │
└──────────────────────────────────────────────────┘
| Module | Purpose |
|---|---|
historical |
Single + batch OHLCV retrieval via WebSocket |
live |
Real-time WebSocket streaming (quotes, charts, studies) |
client |
REST HTTP client (search, news, financial calendar) |
loader |
Event-driven orchestrator (source → fan-out → sinks) |
source |
DataSource trait + TradingView WebSocket adapter |
sink |
EventSink trait + channel, callback, Kafka sinks |
events |
Normalized MarketEvent types (Candle, Quote, News, etc.) |
chart |
Chart session config + Pine Script studies |
quote |
Real-time quote data model + field definitions |
Full API documentation is published on docs.rs. All public types, traits, and modules are documented with examples.
Quick links to key types:
- DataLoader — event-driven orchestrator
- HistoricalClient — historical data
- WebSocketClient — real-time streaming
- Symbol — instrument representation
- Interval — time granularity
For the project roadmap, see ROADMAP.md.
$ claude mcp add tradingview-rs \
-- python -m otcore.mcp_server <graph>