High-performance financial charting library for OHLC data visualization with technical indicators
Project description
Financial Charting Library
A high-performance Python charting library for visualizing OHLC (Open, High, Low, Close) financial data with technical indicators. Built with FastAPI, uPlot, and modern web technologies.
Quick Start
Installation
# Install dependencies
poetry install
Basic Usage
Python API (Recommended)
import pandas as pd
import charting
# Load from CSV file
charting.plot("data/sample.csv")
# Or use a DataFrame directly
df = pd.read_csv("data/sample.csv")
df['timestamp'] = pd.to_datetime(df['timestamp'])
df.set_index('timestamp', inplace=True)
# Add indicators as columns (optional)
df['sma_20'] = df['close'].rolling(20).mean()
df['rsi_14'] = calculate_rsi(df['close'], 14)
# Plot with overlays and subplots
charting.plot(
df,
overlays=['sma_20'], # Indicators on main chart
subplots=['rsi_14'] # Indicators in separate panels
)
Command Line
# Start the server
poetry run python run.py
# Or load data immediately
poetry run python run.py data/sample.csv
The browser will open automatically at http://localhost:8000.
Example Script
See examples/simple_plot.py for a complete example with multiple indicators:
poetry run python examples/simple_plot.py
Library Architecture
Overview
┌─────────────────────────────────────────────────────────────┐
│ User Interface │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Python API │ │ Web Browser │ │ Command Line │ │
│ │charting.plot│ │ (index.html) │ │ (run.py) │ │
│ └──────┬──────┘ └───────┬───────┘ └────────┬─────────┘ │
└─────────┼──────────────────┼───────────────────┼────────────┘
│ │ │
└──────────────────┼───────────────────┘
│
┌────────▼─────────┐
│ FastAPI Server │
│ (src/api/) │
│ - main.py │
│ - routes.py │
│ - processor.py │
└────────┬──────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
┌─────▼──────┐ ┌─────▼──────┐ ┌─────▼──────┐
│ Ingestion │ │ Processing │ │ Frontend │
│ (loader) │ │(indicators)│ │ (uPlot) │
│ - CSV │ │ - RSI │ │ - charts │
│ - Schema │ │ - SMA/EMA │ │ - dividers│
│ - Detect │ │ - MACD │ │ - sync │
└────────────┘ └────────────┘ └────────────┘
Component Wiring
1. Data Ingestion Layer (src/charting/ingestion/)
Purpose: Load and validate CSV data
loader.py: CSV file reading with automatic column detectionschema.py: Data validation and schema enforcementdetector.py: Auto-detect column names (open/high/low/close/volume)mapper.py: Map non-standard column names to expected format
Flow:
CSV File → loader.py → detector.py → mapper.py → Validated DataFrame
2. Processing Layer (src/charting/processing/)
Purpose: Calculate technical indicators and resample data
indicators.py: Technical indicators (RSI, SMA, EMA, Bollinger Bands, MACD, Stochastic)resampler.py: Timeframe conversion (1min → 5min, 15min, 1h, etc.)pivot.py: Data transformation for efficient rendering
Flow:
DataFrame → indicators.py → Indicator Columns
DataFrame → resampler.py → Resampled OHLC
3. API Layer (src/charting/api/)
Purpose: HTTP server and request handling
main.py: FastAPI application, CORS, static file servingroutes.py: API endpoints (/chart-data,/health)processor.py: Request processing, data loading, indicator calculationmodels.py: Pydantic models for request/response validation
Endpoints:
GET /chart-data?filename=X&overlays=Y&subplots=Z
GET /health
GET / (serves frontend)
Flow:
HTTP Request → routes.py → processor.py → ingestion + processing → JSON Response
4. Python API (src/charting/)
Purpose: High-level Python interface
charting.py: MainChartingclass for loading and displaying dataserver.py: Server lifecycle management (start/stop)browser.py: Automatic browser launchinglauncher.py: Process management for servertransformer.py: DataFrame to API format conversion__init__.py: Public API exports (plot()function)
Flow:
charting.plot(df)
→ Charting.load()
→ server.start()
→ transformer.to_csv()
→ browser.open(url)
5. Frontend (src/charting/frontend/)
Purpose: Interactive web-based visualization
index.html: Main HTML structure, controls, layoutapp.js: Application state, event handlers, indicator controlsmulti-chart.js: Multiple synchronized uPlot charts (main + subplots)chart.js: Single chart wrapper (legacy)divider.js: Draggable dividers for resizing panelsdata-client.js: API communication layer
Flow:
index.html loads
→ app.js initializes
→ multi-chart.js creates charts
→ data-client.js fetches data
→ uPlot renders charts
→ divider.js enables resizing
Data Flow Examples
Example 1: Loading CSV via Python API
charting.plot("data/sample.csv", overlays=['sma_20'], subplots=['rsi_14'])
Step-by-step:
charting.plot()→ callsCharting().load()Charting.load()validates DataFrame or reads CSVserver.start()launches FastAPI on available porttransformer.to_csv()saves DataFrame to temp filebrowser.open()opens URL with query params:?filename=X&overlays=sma_20&subplots=rsi_14- Browser loads
index.htmlfrom FastAPI static files app.jsparses URL params and callschartManager.loadAndRender()multi-chart.jsfetches data via/chart-dataendpointroutes.py→processor.pyloads CSV, calculates indicators- JSON data returned to frontend
multi-chart.jscreates uPlot instances (main chart + subplots)divider.jscreates draggable dividers between charts- Charts synchronized for zoom/pan
Example 2: Loading CSV via Web Interface
User enters "sample.csv" and clicks "Load Chart"
Step-by-step:
- User types filename in input field
app.jsevent handler captures clickapp.loadChart()→chartManager.loadAndRender(filename)data-client.jsconstructs API URL:/chart-data?filename=sample.csv- FastAPI receives request in
routes.py processor.process_chart_request()validates filenameloader.pyreads CSV fromdata/directorydetector.pyauto-detects column namesindicators.pycalculates any requested indicatorsprocessor.pyformats data as uPlot-compatible JSON- Response sent back to browser
multi-chart.jsrenders chartsapp.buildIndicatorControls()creates checkboxes for toggling indicators
Example 3: Adding Indicators Dynamically
User checks "RSI 14" checkbox
Step-by-step:
- Checkbox change event captured by
app.js chartManager.toggleSubplot('rsi_14')called- Updates
visibleSubplotsset - Updates
config.subplotsarray - Calls
multi-chart.initialize()to re-render - Fetches data with new subplot:
/chart-data?subplots=rsi_14 processor.pycalculates RSI if not in CSV- Returns data with RSI column
multi-chart.jscreates new subplot paneldivider.jsadds divider above new subplot- All charts re-synchronized
API Reference
Python API
charting.plot(data, **kwargs)
Plot financial data with a single command.
Parameters:
data(DataFrame | str): DataFrame with OHLC data or path to CSV fileoverlays(list[str], optional): Indicators to plot on main chart (e.g.,['sma_20', 'ema_12'])subplots(list[str], optional): Indicators to plot in separate panels (e.g.,['rsi_14', 'macd'])
Returns: URL string where chart is served
Example:
import charting
charting.plot("data/sample.csv", overlays=['sma_20'], subplots=['rsi_14'])
Charting Class
Advanced usage with more control:
from charting import Charting
chart = Charting()
url = chart.load(df, overlays=['sma_20'], subplots=['rsi_14'])
# Server keeps running...
chart.close() # Stop server
Methods:
load(df, **kwargs): Load data and start serverclose(): Stop the serveris_running(): Check if server is running
HTTP API
GET /chart-data
Fetch chart data with optional indicators and timeframe resampling.
Query Parameters:
filename(required): CSV file name (relative todata/directory)overlays(optional): Comma-separated indicator names (e.g.,sma_20,ema_12)subplots(optional): Comma-separated indicator names (e.g.,rsi_14,macd)timeframe(optional): Resample to timeframe (e.g.,5min,1h,1D)
Response:
{
"data": [
[1609459200000, 1609459260000, ...], // timestamps
[100.0, 101.0, ...], // open
[102.0, 103.0, ...], // high
[99.0, 100.5, ...], // low
[101.0, 102.0, ...], // close
[1000, 1500, ...], // volume
[100.5, 101.2, ...], // sma_20 (if requested)
[45.2, 52.1, ...] // rsi_14 (if requested)
],
"metadata": {
"overlays": ["sma_20"],
"subplots": ["rsi_14"],
"filename": "sample.csv",
"timeframe": "1min"
}
}
GET /health
Health check endpoint.
Response:
{
"status": "ok",
"version": "0.1.0"
}
Indicators
Available Indicators
Overlay Indicators (on main chart)
- SMA - Simple Moving Average
sma_10,sma_20,sma_50,sma_200
- EMA - Exponential Moving Average
ema_9,ema_12,ema_21,ema_26
- Bollinger Bands
bb_upper,bb_middle,bb_lower
Subplot Indicators (separate panels)
- RSI - Relative Strength Index
rsi_14(default period: 14)
- MACD - Moving Average Convergence Divergence
macd,macd_signal,macd_hist
- Stochastic Oscillator
stoch_k,stoch_d
- Volume SMA
volume_sma(volume with 20-period SMA overlay)
Adding Custom Indicators
To add indicators to your data:
import pandas as pd
# Load data
df = pd.read_csv("data/sample.csv")
df['timestamp'] = pd.to_datetime(df['timestamp'])
df.set_index('timestamp', inplace=True)
# Calculate indicators
df['sma_20'] = df['close'].rolling(window=20).mean()
df['ema_12'] = df['close'].ewm(span=12, adjust=False).mean()
# Calculate RSI
def calculate_rsi(series, period=14):
delta = series.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
rs = gain / loss
return 100 - (100 / (1 + rs))
df['rsi_14'] = calculate_rsi(df['close'], 14)
# Plot with indicators
import charting
charting.plot(df, overlays=['sma_20', 'ema_12'], subplots=['rsi_14'])
Frontend Features
Interactive Controls
- Indicator Toggles: Check/uncheck indicators to show/hide them
- Draggable Dividers: Resize chart panels by dragging horizontal dividers
- Synchronized Zoom: Zoom/pan on any chart affects all charts
- Synchronized Crosshair: Hover cursor synchronized across all panels
- Timeframe Selection: Resample data to different timeframes
- Scrollable Layout: Page scrolls when you have many indicators
Keyboard Shortcuts
- Scroll: Zoom in/out
- Click + Drag: Pan chart
- Hover: Show crosshair with values
Project Structure
charting/
├── src/
│ └── charting/ # Main package
│ ├── __init__.py # Public API (plot function)
│ ├── charting.py # Charting class
│ ├── server.py # Server management
│ ├── browser.py # Browser launcher
│ ├── launcher.py # Process management
│ ├── transformer.py # Data transformation
│ ├── detector.py # Column detection
│ ├── mapper.py # Column mapping
│ │
│ ├── api/ # FastAPI backend
│ │ ├── main.py # App entry, static serving
│ │ ├── routes.py # API endpoints
│ │ ├── processor.py # Request processing
│ │ ├── models.py # Pydantic models
│ │ └── exceptions.py # Custom exceptions
│ │
│ ├── ingestion/ # Data loading
│ │ ├── loader.py # CSV reader
│ │ └── schema.py # Validation
│ │
│ ├── processing/ # Data processing
│ │ ├── indicators.py # Technical indicators
│ │ ├── resampler.py # Timeframe conversion
│ │ └── pivot.py # Data transformation
│ │
│ └── frontend/ # Web UI
│ ├── index.html # Main page
│ ├── app.js # Application logic
│ ├── multi-chart.js # Multi-chart manager
│ ├── chart.js # Single chart (legacy)
│ ├── divider.js # Resizable dividers
│ ├── divider.css # Divider styles
│ ├── data-client.js # API client
│ ├── layout-manager.js # Layout utilities
│ └── ...
│
├── examples/
│ ├── simple_plot.py # Basic usage example
│ └── demo_all_features.py # Advanced example
│
├── data/ # Sample CSV files
│ ├── sample.csv # Moderate volatility
│ ├── crypto.csv # High volatility
│ └── stock.csv # Low volatility
│
├── tests/ # Test suite
│ ├── test_api_*.py # API tests
│ ├── test_indicators_*.py # Indicator tests
│ ├── test_frontend_*.py # Frontend tests
│ └── ...
│
├── pyproject.toml # Poetry config
├── README.md # This file
└── run.py # CLI launcher
Development
Running Tests
# Run all tests
poetry run pytest
# Run specific test file
poetry run pytest tests/test_indicators.py
# Run with coverage
poetry run pytest --cov=src --cov-report=html
# Run only fast tests (skip integration)
poetry run pytest -m "not integration"
Code Quality
# Format code
poetry run black src tests
# Check types
poetry run mypy src
# Lint
poetry run ruff check src tests
Adding Dependencies
# Production dependency
poetry add package-name
# Development dependency
poetry add --group dev package-name
Configuration
Server Configuration
The server automatically:
- Finds an available port (starts at 8000, increments if busy)
- Enables CORS for local development
- Serves static files from
src/charting/frontend/ - Handles graceful shutdown
Data Requirements
CSV files must have:
- Required columns: timestamp, open, high, low, close
- Optional columns: volume, any indicator columns
- Timestamp format: ISO8601, Unix timestamp, or parseable date string
Example CSV:
timestamp,open,high,low,close,volume
2024-01-01 00:00:00,100.0,102.0,99.0,101.0,1000
2024-01-01 00:01:00,101.0,103.0,100.5,102.0,1500
Performance
Benchmarks
- Render: 500k+ points at 60fps
- Load: 100MB CSV in <2 seconds
- Interaction: <16ms zoom/pan latency
- Memory: Efficient columnar data storage
Optimization Tips
- Use appropriate timeframes: Higher timeframes = less data = faster rendering
- Limit indicators: Each indicator adds a render pass
- CSV optimization: Pre-calculate indicators and save to CSV
- Batch operations: Load multiple indicators at once instead of one-by-one
Troubleshooting
Common Issues
"Failed to load chart"
- Ensure CSV file exists in
data/directory - Check CSV format (must have OHLC columns)
- Verify server is running (
http://localhost:8000/health)
"Module not found"
- Run
poetry installto install dependencies - Ensure you're using
poetry run pythonorpoetry shell
"Port already in use"
- Server auto-increments to find available port
- Check terminal output for actual port number
- Or manually kill process:
lsof -ti:8000 | xargs kill -9
Charts not rendering
- Open browser console (F12) to check for JavaScript errors
- Verify data is being returned:
http://localhost:8000/chart-data?filename=sample.csv - Check that uPlot CDN is accessible
Indicators not showing
- Ensure indicator data exists in CSV or can be calculated
- Check browser console for errors
- Verify indicator names match expected format (e.g.,
rsi_14, notRSI-14)
Additional Resources
Sample Data
Generate custom sample data:
poetry run python scripts/generate_sample_data.py \
--output data/custom.csv \
--periods 10000 \
--freq 1min \
--price 100.0 \
--volatility 0.02
API Documentation
Interactive API docs available at:
- Swagger UI:
http://localhost:8000/docs - ReDoc:
http://localhost:8000/redoc
License
MIT License - see LICENSE file for details
Contributing
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
Support
For issues, questions, or suggestions:
- Open an issue on GitHub
- Check existing issues for solutions
- Review documentation and examples
Built with ❤️ using FastAPI, uPlot, and Python
Built with ❤️ from Dubai
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pycharting-0.1.0.tar.gz.
File metadata
- Download URL: pycharting-0.1.0.tar.gz
- Upload date:
- Size: 81.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.8.2 CPython/3.12.3 Windows/11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2d6c3f40a4228452bbc06fcc40ad757b5929f03e6993504a29636baacf091bf9
|
|
| MD5 |
ca79575644982033676f34d7006dd1cf
|
|
| BLAKE2b-256 |
aea587a4d48dfd8eb7ad9d21db8fd37d47a695337c30d98f5ccac8e9afe58d2d
|
File details
Details for the file pycharting-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pycharting-0.1.0-py3-none-any.whl
- Upload date:
- Size: 87.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.8.2 CPython/3.12.3 Windows/11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a67273b25e5ee1d052a04e36c2d938b989e224eca8b8ba6f50baa3bdd6c1807
|
|
| MD5 |
f030e89022b7b9428fc7d018ad7e4010
|
|
| BLAKE2b-256 |
f1c2b73be371bf58e3f712a73468e036bec6ad0b74c75d3bfc4f8751692837da
|