pycharting 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pycharting-0.1.0/PKG-INFO +676 -0
- pycharting-0.1.0/README.md +649 -0
- pycharting-0.1.0/pyproject.toml +39 -0
- pycharting-0.1.0/src/charting/__init__.py +80 -0
- pycharting-0.1.0/src/charting/api/__init__.py +1 -0
- pycharting-0.1.0/src/charting/api/exceptions.py +41 -0
- pycharting-0.1.0/src/charting/api/main.py +134 -0
- pycharting-0.1.0/src/charting/api/models.py +156 -0
- pycharting-0.1.0/src/charting/api/processor.py +415 -0
- pycharting-0.1.0/src/charting/api/routes.py +166 -0
- pycharting-0.1.0/src/charting/browser.py +145 -0
- pycharting-0.1.0/src/charting/charting.py +420 -0
- pycharting-0.1.0/src/charting/detector.py +576 -0
- pycharting-0.1.0/src/charting/frontend/app.js +431 -0
- pycharting-0.1.0/src/charting/frontend/chart.js +597 -0
- pycharting-0.1.0/src/charting/frontend/data-client.js +224 -0
- pycharting-0.1.0/src/charting/frontend/divider.css +83 -0
- pycharting-0.1.0/src/charting/frontend/divider.js +455 -0
- pycharting-0.1.0/src/charting/frontend/index.html +215 -0
- pycharting-0.1.0/src/charting/frontend/layout-manager.js +683 -0
- pycharting-0.1.0/src/charting/frontend/multi-chart.js +1779 -0
- pycharting-0.1.0/src/charting/ingestion/__init__.py +2 -0
- pycharting-0.1.0/src/charting/ingestion/loader.py +314 -0
- pycharting-0.1.0/src/charting/ingestion/schema.py +67 -0
- pycharting-0.1.0/src/charting/launcher.py +212 -0
- pycharting-0.1.0/src/charting/mapper.py +316 -0
- pycharting-0.1.0/src/charting/processing/__init__.py +2 -0
- pycharting-0.1.0/src/charting/processing/indicators.py +248 -0
- pycharting-0.1.0/src/charting/processing/pivot.py +218 -0
- pycharting-0.1.0/src/charting/processing/resampler.py +422 -0
- pycharting-0.1.0/src/charting/server.py +198 -0
- pycharting-0.1.0/src/charting/transformer.py +222 -0
|
@@ -0,0 +1,676 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pycharting
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: High-performance financial charting library for OHLC data visualization with technical indicators
|
|
5
|
+
Home-page: https://github.com/alihaskar/pycharting
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: charting,finance,ohlc,technical-indicators,trading,visualization
|
|
8
|
+
Author: ali askar
|
|
9
|
+
Author-email: 26202651+alihaskar@users.noreply.github.com
|
|
10
|
+
Requires-Python: >=3.12,<4.0
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
19
|
+
Requires-Dist: fastapi (>=0.123.4,<0.124.0)
|
|
20
|
+
Requires-Dist: numpy (>=2.3.5,<3.0.0)
|
|
21
|
+
Requires-Dist: pandas (>=2.3.3,<3.0.0)
|
|
22
|
+
Requires-Dist: pydantic (>=2.12.5,<3.0.0)
|
|
23
|
+
Requires-Dist: pytz (>=2025.2,<2026.0)
|
|
24
|
+
Requires-Dist: uvicorn (>=0.38.0,<0.39.0)
|
|
25
|
+
Project-URL: Repository, https://github.com/alihaskar/pycharting
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# Financial Charting Library
|
|
29
|
+
|
|
30
|
+
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.
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
### Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Install dependencies
|
|
38
|
+
poetry install
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Basic Usage
|
|
42
|
+
|
|
43
|
+
#### Python API (Recommended)
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import pandas as pd
|
|
47
|
+
import charting
|
|
48
|
+
|
|
49
|
+
# Load from CSV file
|
|
50
|
+
charting.plot("data/sample.csv")
|
|
51
|
+
|
|
52
|
+
# Or use a DataFrame directly
|
|
53
|
+
df = pd.read_csv("data/sample.csv")
|
|
54
|
+
df['timestamp'] = pd.to_datetime(df['timestamp'])
|
|
55
|
+
df.set_index('timestamp', inplace=True)
|
|
56
|
+
|
|
57
|
+
# Add indicators as columns (optional)
|
|
58
|
+
df['sma_20'] = df['close'].rolling(20).mean()
|
|
59
|
+
df['rsi_14'] = calculate_rsi(df['close'], 14)
|
|
60
|
+
|
|
61
|
+
# Plot with overlays and subplots
|
|
62
|
+
charting.plot(
|
|
63
|
+
df,
|
|
64
|
+
overlays=['sma_20'], # Indicators on main chart
|
|
65
|
+
subplots=['rsi_14'] # Indicators in separate panels
|
|
66
|
+
)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
#### Command Line
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Start the server
|
|
73
|
+
poetry run python run.py
|
|
74
|
+
|
|
75
|
+
# Or load data immediately
|
|
76
|
+
poetry run python run.py data/sample.csv
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The browser will open automatically at `http://localhost:8000`.
|
|
80
|
+
|
|
81
|
+
### Example Script
|
|
82
|
+
|
|
83
|
+
See `examples/simple_plot.py` for a complete example with multiple indicators:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
poetry run python examples/simple_plot.py
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Library Architecture
|
|
92
|
+
|
|
93
|
+
### Overview
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
97
|
+
│ User Interface │
|
|
98
|
+
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │
|
|
99
|
+
│ │ Python API │ │ Web Browser │ │ Command Line │ │
|
|
100
|
+
│ │charting.plot│ │ (index.html) │ │ (run.py) │ │
|
|
101
|
+
│ └──────┬──────┘ └───────┬───────┘ └────────┬─────────┘ │
|
|
102
|
+
└─────────┼──────────────────┼───────────────────┼────────────┘
|
|
103
|
+
│ │ │
|
|
104
|
+
└──────────────────┼───────────────────┘
|
|
105
|
+
│
|
|
106
|
+
┌────────▼─────────┐
|
|
107
|
+
│ FastAPI Server │
|
|
108
|
+
│ (src/api/) │
|
|
109
|
+
│ - main.py │
|
|
110
|
+
│ - routes.py │
|
|
111
|
+
│ - processor.py │
|
|
112
|
+
└────────┬──────────┘
|
|
113
|
+
│
|
|
114
|
+
┌──────────────────┼──────────────────┐
|
|
115
|
+
│ │ │
|
|
116
|
+
┌─────▼──────┐ ┌─────▼──────┐ ┌─────▼──────┐
|
|
117
|
+
│ Ingestion │ │ Processing │ │ Frontend │
|
|
118
|
+
│ (loader) │ │(indicators)│ │ (uPlot) │
|
|
119
|
+
│ - CSV │ │ - RSI │ │ - charts │
|
|
120
|
+
│ - Schema │ │ - SMA/EMA │ │ - dividers│
|
|
121
|
+
│ - Detect │ │ - MACD │ │ - sync │
|
|
122
|
+
└────────────┘ └────────────┘ └────────────┘
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Component Wiring
|
|
126
|
+
|
|
127
|
+
#### 1. **Data Ingestion Layer** (`src/charting/ingestion/`)
|
|
128
|
+
|
|
129
|
+
**Purpose**: Load and validate CSV data
|
|
130
|
+
|
|
131
|
+
- **`loader.py`**: CSV file reading with automatic column detection
|
|
132
|
+
- **`schema.py`**: Data validation and schema enforcement
|
|
133
|
+
- **`detector.py`**: Auto-detect column names (open/high/low/close/volume)
|
|
134
|
+
- **`mapper.py`**: Map non-standard column names to expected format
|
|
135
|
+
|
|
136
|
+
**Flow**:
|
|
137
|
+
```
|
|
138
|
+
CSV File → loader.py → detector.py → mapper.py → Validated DataFrame
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### 2. **Processing Layer** (`src/charting/processing/`)
|
|
142
|
+
|
|
143
|
+
**Purpose**: Calculate technical indicators and resample data
|
|
144
|
+
|
|
145
|
+
- **`indicators.py`**: Technical indicators (RSI, SMA, EMA, Bollinger Bands, MACD, Stochastic)
|
|
146
|
+
- **`resampler.py`**: Timeframe conversion (1min → 5min, 15min, 1h, etc.)
|
|
147
|
+
- **`pivot.py`**: Data transformation for efficient rendering
|
|
148
|
+
|
|
149
|
+
**Flow**:
|
|
150
|
+
```
|
|
151
|
+
DataFrame → indicators.py → Indicator Columns
|
|
152
|
+
DataFrame → resampler.py → Resampled OHLC
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### 3. **API Layer** (`src/charting/api/`)
|
|
156
|
+
|
|
157
|
+
**Purpose**: HTTP server and request handling
|
|
158
|
+
|
|
159
|
+
- **`main.py`**: FastAPI application, CORS, static file serving
|
|
160
|
+
- **`routes.py`**: API endpoints (`/chart-data`, `/health`)
|
|
161
|
+
- **`processor.py`**: Request processing, data loading, indicator calculation
|
|
162
|
+
- **`models.py`**: Pydantic models for request/response validation
|
|
163
|
+
|
|
164
|
+
**Endpoints**:
|
|
165
|
+
```
|
|
166
|
+
GET /chart-data?filename=X&overlays=Y&subplots=Z
|
|
167
|
+
GET /health
|
|
168
|
+
GET / (serves frontend)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Flow**:
|
|
172
|
+
```
|
|
173
|
+
HTTP Request → routes.py → processor.py → ingestion + processing → JSON Response
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### 4. **Python API** (`src/charting/`)
|
|
177
|
+
|
|
178
|
+
**Purpose**: High-level Python interface
|
|
179
|
+
|
|
180
|
+
- **`charting.py`**: Main `Charting` class for loading and displaying data
|
|
181
|
+
- **`server.py`**: Server lifecycle management (start/stop)
|
|
182
|
+
- **`browser.py`**: Automatic browser launching
|
|
183
|
+
- **`launcher.py`**: Process management for server
|
|
184
|
+
- **`transformer.py`**: DataFrame to API format conversion
|
|
185
|
+
- **`__init__.py`**: Public API exports (`plot()` function)
|
|
186
|
+
|
|
187
|
+
**Flow**:
|
|
188
|
+
```python
|
|
189
|
+
charting.plot(df)
|
|
190
|
+
→ Charting.load()
|
|
191
|
+
→ server.start()
|
|
192
|
+
→ transformer.to_csv()
|
|
193
|
+
→ browser.open(url)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### 5. **Frontend** (`src/charting/frontend/`)
|
|
197
|
+
|
|
198
|
+
**Purpose**: Interactive web-based visualization
|
|
199
|
+
|
|
200
|
+
- **`index.html`**: Main HTML structure, controls, layout
|
|
201
|
+
- **`app.js`**: Application state, event handlers, indicator controls
|
|
202
|
+
- **`multi-chart.js`**: Multiple synchronized uPlot charts (main + subplots)
|
|
203
|
+
- **`chart.js`**: Single chart wrapper (legacy)
|
|
204
|
+
- **`divider.js`**: Draggable dividers for resizing panels
|
|
205
|
+
- **`data-client.js`**: API communication layer
|
|
206
|
+
|
|
207
|
+
**Flow**:
|
|
208
|
+
```
|
|
209
|
+
index.html loads
|
|
210
|
+
→ app.js initializes
|
|
211
|
+
→ multi-chart.js creates charts
|
|
212
|
+
→ data-client.js fetches data
|
|
213
|
+
→ uPlot renders charts
|
|
214
|
+
→ divider.js enables resizing
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Data Flow Examples
|
|
220
|
+
|
|
221
|
+
### Example 1: Loading CSV via Python API
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
charting.plot("data/sample.csv", overlays=['sma_20'], subplots=['rsi_14'])
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Step-by-step**:
|
|
228
|
+
1. `charting.plot()` → calls `Charting().load()`
|
|
229
|
+
2. `Charting.load()` validates DataFrame or reads CSV
|
|
230
|
+
3. `server.start()` launches FastAPI on available port
|
|
231
|
+
4. `transformer.to_csv()` saves DataFrame to temp file
|
|
232
|
+
5. `browser.open()` opens URL with query params: `?filename=X&overlays=sma_20&subplots=rsi_14`
|
|
233
|
+
6. Browser loads `index.html` from FastAPI static files
|
|
234
|
+
7. `app.js` parses URL params and calls `chartManager.loadAndRender()`
|
|
235
|
+
8. `multi-chart.js` fetches data via `/chart-data` endpoint
|
|
236
|
+
9. `routes.py` → `processor.py` loads CSV, calculates indicators
|
|
237
|
+
10. JSON data returned to frontend
|
|
238
|
+
11. `multi-chart.js` creates uPlot instances (main chart + subplots)
|
|
239
|
+
12. `divider.js` creates draggable dividers between charts
|
|
240
|
+
13. Charts synchronized for zoom/pan
|
|
241
|
+
|
|
242
|
+
### Example 2: Loading CSV via Web Interface
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
User enters "sample.csv" and clicks "Load Chart"
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
**Step-by-step**:
|
|
249
|
+
1. User types filename in input field
|
|
250
|
+
2. `app.js` event handler captures click
|
|
251
|
+
3. `app.loadChart()` → `chartManager.loadAndRender(filename)`
|
|
252
|
+
4. `data-client.js` constructs API URL: `/chart-data?filename=sample.csv`
|
|
253
|
+
5. FastAPI receives request in `routes.py`
|
|
254
|
+
6. `processor.process_chart_request()` validates filename
|
|
255
|
+
7. `loader.py` reads CSV from `data/` directory
|
|
256
|
+
8. `detector.py` auto-detects column names
|
|
257
|
+
9. `indicators.py` calculates any requested indicators
|
|
258
|
+
10. `processor.py` formats data as uPlot-compatible JSON
|
|
259
|
+
11. Response sent back to browser
|
|
260
|
+
12. `multi-chart.js` renders charts
|
|
261
|
+
13. `app.buildIndicatorControls()` creates checkboxes for toggling indicators
|
|
262
|
+
|
|
263
|
+
### Example 3: Adding Indicators Dynamically
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
User checks "RSI 14" checkbox
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**Step-by-step**:
|
|
270
|
+
1. Checkbox change event captured by `app.js`
|
|
271
|
+
2. `chartManager.toggleSubplot('rsi_14')` called
|
|
272
|
+
3. Updates `visibleSubplots` set
|
|
273
|
+
4. Updates `config.subplots` array
|
|
274
|
+
5. Calls `multi-chart.initialize()` to re-render
|
|
275
|
+
6. Fetches data with new subplot: `/chart-data?subplots=rsi_14`
|
|
276
|
+
7. `processor.py` calculates RSI if not in CSV
|
|
277
|
+
8. Returns data with RSI column
|
|
278
|
+
9. `multi-chart.js` creates new subplot panel
|
|
279
|
+
10. `divider.js` adds divider above new subplot
|
|
280
|
+
11. All charts re-synchronized
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## API Reference
|
|
285
|
+
|
|
286
|
+
### Python API
|
|
287
|
+
|
|
288
|
+
#### `charting.plot(data, **kwargs)`
|
|
289
|
+
|
|
290
|
+
Plot financial data with a single command.
|
|
291
|
+
|
|
292
|
+
**Parameters**:
|
|
293
|
+
- `data` (DataFrame | str): DataFrame with OHLC data or path to CSV file
|
|
294
|
+
- `overlays` (list[str], optional): Indicators to plot on main chart (e.g., `['sma_20', 'ema_12']`)
|
|
295
|
+
- `subplots` (list[str], optional): Indicators to plot in separate panels (e.g., `['rsi_14', 'macd']`)
|
|
296
|
+
|
|
297
|
+
**Returns**: URL string where chart is served
|
|
298
|
+
|
|
299
|
+
**Example**:
|
|
300
|
+
```python
|
|
301
|
+
import charting
|
|
302
|
+
charting.plot("data/sample.csv", overlays=['sma_20'], subplots=['rsi_14'])
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
#### `Charting` Class
|
|
306
|
+
|
|
307
|
+
Advanced usage with more control:
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
from charting import Charting
|
|
311
|
+
|
|
312
|
+
chart = Charting()
|
|
313
|
+
url = chart.load(df, overlays=['sma_20'], subplots=['rsi_14'])
|
|
314
|
+
# Server keeps running...
|
|
315
|
+
chart.close() # Stop server
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Methods**:
|
|
319
|
+
- `load(df, **kwargs)`: Load data and start server
|
|
320
|
+
- `close()`: Stop the server
|
|
321
|
+
- `is_running()`: Check if server is running
|
|
322
|
+
|
|
323
|
+
### HTTP API
|
|
324
|
+
|
|
325
|
+
#### `GET /chart-data`
|
|
326
|
+
|
|
327
|
+
Fetch chart data with optional indicators and timeframe resampling.
|
|
328
|
+
|
|
329
|
+
**Query Parameters**:
|
|
330
|
+
- `filename` (required): CSV file name (relative to `data/` directory)
|
|
331
|
+
- `overlays` (optional): Comma-separated indicator names (e.g., `sma_20,ema_12`)
|
|
332
|
+
- `subplots` (optional): Comma-separated indicator names (e.g., `rsi_14,macd`)
|
|
333
|
+
- `timeframe` (optional): Resample to timeframe (e.g., `5min`, `1h`, `1D`)
|
|
334
|
+
|
|
335
|
+
**Response**:
|
|
336
|
+
```json
|
|
337
|
+
{
|
|
338
|
+
"data": [
|
|
339
|
+
[1609459200000, 1609459260000, ...], // timestamps
|
|
340
|
+
[100.0, 101.0, ...], // open
|
|
341
|
+
[102.0, 103.0, ...], // high
|
|
342
|
+
[99.0, 100.5, ...], // low
|
|
343
|
+
[101.0, 102.0, ...], // close
|
|
344
|
+
[1000, 1500, ...], // volume
|
|
345
|
+
[100.5, 101.2, ...], // sma_20 (if requested)
|
|
346
|
+
[45.2, 52.1, ...] // rsi_14 (if requested)
|
|
347
|
+
],
|
|
348
|
+
"metadata": {
|
|
349
|
+
"overlays": ["sma_20"],
|
|
350
|
+
"subplots": ["rsi_14"],
|
|
351
|
+
"filename": "sample.csv",
|
|
352
|
+
"timeframe": "1min"
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
#### `GET /health`
|
|
358
|
+
|
|
359
|
+
Health check endpoint.
|
|
360
|
+
|
|
361
|
+
**Response**:
|
|
362
|
+
```json
|
|
363
|
+
{
|
|
364
|
+
"status": "ok",
|
|
365
|
+
"version": "0.1.0"
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## Indicators
|
|
372
|
+
|
|
373
|
+
### Available Indicators
|
|
374
|
+
|
|
375
|
+
#### Overlay Indicators (on main chart)
|
|
376
|
+
- **SMA** - Simple Moving Average
|
|
377
|
+
- `sma_10`, `sma_20`, `sma_50`, `sma_200`
|
|
378
|
+
- **EMA** - Exponential Moving Average
|
|
379
|
+
- `ema_9`, `ema_12`, `ema_21`, `ema_26`
|
|
380
|
+
- **Bollinger Bands**
|
|
381
|
+
- `bb_upper`, `bb_middle`, `bb_lower`
|
|
382
|
+
|
|
383
|
+
#### Subplot Indicators (separate panels)
|
|
384
|
+
- **RSI** - Relative Strength Index
|
|
385
|
+
- `rsi_14` (default period: 14)
|
|
386
|
+
- **MACD** - Moving Average Convergence Divergence
|
|
387
|
+
- `macd`, `macd_signal`, `macd_hist`
|
|
388
|
+
- **Stochastic Oscillator**
|
|
389
|
+
- `stoch_k`, `stoch_d`
|
|
390
|
+
- **Volume SMA**
|
|
391
|
+
- `volume_sma` (volume with 20-period SMA overlay)
|
|
392
|
+
|
|
393
|
+
### Adding Custom Indicators
|
|
394
|
+
|
|
395
|
+
To add indicators to your data:
|
|
396
|
+
|
|
397
|
+
```python
|
|
398
|
+
import pandas as pd
|
|
399
|
+
|
|
400
|
+
# Load data
|
|
401
|
+
df = pd.read_csv("data/sample.csv")
|
|
402
|
+
df['timestamp'] = pd.to_datetime(df['timestamp'])
|
|
403
|
+
df.set_index('timestamp', inplace=True)
|
|
404
|
+
|
|
405
|
+
# Calculate indicators
|
|
406
|
+
df['sma_20'] = df['close'].rolling(window=20).mean()
|
|
407
|
+
df['ema_12'] = df['close'].ewm(span=12, adjust=False).mean()
|
|
408
|
+
|
|
409
|
+
# Calculate RSI
|
|
410
|
+
def calculate_rsi(series, period=14):
|
|
411
|
+
delta = series.diff()
|
|
412
|
+
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
|
|
413
|
+
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
|
|
414
|
+
rs = gain / loss
|
|
415
|
+
return 100 - (100 / (1 + rs))
|
|
416
|
+
|
|
417
|
+
df['rsi_14'] = calculate_rsi(df['close'], 14)
|
|
418
|
+
|
|
419
|
+
# Plot with indicators
|
|
420
|
+
import charting
|
|
421
|
+
charting.plot(df, overlays=['sma_20', 'ema_12'], subplots=['rsi_14'])
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## Frontend Features
|
|
427
|
+
|
|
428
|
+
### Interactive Controls
|
|
429
|
+
|
|
430
|
+
1. **Indicator Toggles**: Check/uncheck indicators to show/hide them
|
|
431
|
+
2. **Draggable Dividers**: Resize chart panels by dragging horizontal dividers
|
|
432
|
+
3. **Synchronized Zoom**: Zoom/pan on any chart affects all charts
|
|
433
|
+
4. **Synchronized Crosshair**: Hover cursor synchronized across all panels
|
|
434
|
+
5. **Timeframe Selection**: Resample data to different timeframes
|
|
435
|
+
6. **Scrollable Layout**: Page scrolls when you have many indicators
|
|
436
|
+
|
|
437
|
+
### Keyboard Shortcuts
|
|
438
|
+
|
|
439
|
+
- **Scroll**: Zoom in/out
|
|
440
|
+
- **Click + Drag**: Pan chart
|
|
441
|
+
- **Hover**: Show crosshair with values
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## Project Structure
|
|
446
|
+
|
|
447
|
+
```
|
|
448
|
+
charting/
|
|
449
|
+
├── src/
|
|
450
|
+
│ └── charting/ # Main package
|
|
451
|
+
│ ├── __init__.py # Public API (plot function)
|
|
452
|
+
│ ├── charting.py # Charting class
|
|
453
|
+
│ ├── server.py # Server management
|
|
454
|
+
│ ├── browser.py # Browser launcher
|
|
455
|
+
│ ├── launcher.py # Process management
|
|
456
|
+
│ ├── transformer.py # Data transformation
|
|
457
|
+
│ ├── detector.py # Column detection
|
|
458
|
+
│ ├── mapper.py # Column mapping
|
|
459
|
+
│ │
|
|
460
|
+
│ ├── api/ # FastAPI backend
|
|
461
|
+
│ │ ├── main.py # App entry, static serving
|
|
462
|
+
│ │ ├── routes.py # API endpoints
|
|
463
|
+
│ │ ├── processor.py # Request processing
|
|
464
|
+
│ │ ├── models.py # Pydantic models
|
|
465
|
+
│ │ └── exceptions.py # Custom exceptions
|
|
466
|
+
│ │
|
|
467
|
+
│ ├── ingestion/ # Data loading
|
|
468
|
+
│ │ ├── loader.py # CSV reader
|
|
469
|
+
│ │ └── schema.py # Validation
|
|
470
|
+
│ │
|
|
471
|
+
│ ├── processing/ # Data processing
|
|
472
|
+
│ │ ├── indicators.py # Technical indicators
|
|
473
|
+
│ │ ├── resampler.py # Timeframe conversion
|
|
474
|
+
│ │ └── pivot.py # Data transformation
|
|
475
|
+
│ │
|
|
476
|
+
│ └── frontend/ # Web UI
|
|
477
|
+
│ ├── index.html # Main page
|
|
478
|
+
│ ├── app.js # Application logic
|
|
479
|
+
│ ├── multi-chart.js # Multi-chart manager
|
|
480
|
+
│ ├── chart.js # Single chart (legacy)
|
|
481
|
+
│ ├── divider.js # Resizable dividers
|
|
482
|
+
│ ├── divider.css # Divider styles
|
|
483
|
+
│ ├── data-client.js # API client
|
|
484
|
+
│ ├── layout-manager.js # Layout utilities
|
|
485
|
+
│ └── ...
|
|
486
|
+
│
|
|
487
|
+
├── examples/
|
|
488
|
+
│ ├── simple_plot.py # Basic usage example
|
|
489
|
+
│ └── demo_all_features.py # Advanced example
|
|
490
|
+
│
|
|
491
|
+
├── data/ # Sample CSV files
|
|
492
|
+
│ ├── sample.csv # Moderate volatility
|
|
493
|
+
│ ├── crypto.csv # High volatility
|
|
494
|
+
│ └── stock.csv # Low volatility
|
|
495
|
+
│
|
|
496
|
+
├── tests/ # Test suite
|
|
497
|
+
│ ├── test_api_*.py # API tests
|
|
498
|
+
│ ├── test_indicators_*.py # Indicator tests
|
|
499
|
+
│ ├── test_frontend_*.py # Frontend tests
|
|
500
|
+
│ └── ...
|
|
501
|
+
│
|
|
502
|
+
├── pyproject.toml # Poetry config
|
|
503
|
+
├── README.md # This file
|
|
504
|
+
└── run.py # CLI launcher
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
## Development
|
|
510
|
+
|
|
511
|
+
### Running Tests
|
|
512
|
+
|
|
513
|
+
```bash
|
|
514
|
+
# Run all tests
|
|
515
|
+
poetry run pytest
|
|
516
|
+
|
|
517
|
+
# Run specific test file
|
|
518
|
+
poetry run pytest tests/test_indicators.py
|
|
519
|
+
|
|
520
|
+
# Run with coverage
|
|
521
|
+
poetry run pytest --cov=src --cov-report=html
|
|
522
|
+
|
|
523
|
+
# Run only fast tests (skip integration)
|
|
524
|
+
poetry run pytest -m "not integration"
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### Code Quality
|
|
528
|
+
|
|
529
|
+
```bash
|
|
530
|
+
# Format code
|
|
531
|
+
poetry run black src tests
|
|
532
|
+
|
|
533
|
+
# Check types
|
|
534
|
+
poetry run mypy src
|
|
535
|
+
|
|
536
|
+
# Lint
|
|
537
|
+
poetry run ruff check src tests
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Adding Dependencies
|
|
541
|
+
|
|
542
|
+
```bash
|
|
543
|
+
# Production dependency
|
|
544
|
+
poetry add package-name
|
|
545
|
+
|
|
546
|
+
# Development dependency
|
|
547
|
+
poetry add --group dev package-name
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
---
|
|
551
|
+
|
|
552
|
+
## Configuration
|
|
553
|
+
|
|
554
|
+
### Server Configuration
|
|
555
|
+
|
|
556
|
+
The server automatically:
|
|
557
|
+
- Finds an available port (starts at 8000, increments if busy)
|
|
558
|
+
- Enables CORS for local development
|
|
559
|
+
- Serves static files from `src/charting/frontend/`
|
|
560
|
+
- Handles graceful shutdown
|
|
561
|
+
|
|
562
|
+
### Data Requirements
|
|
563
|
+
|
|
564
|
+
CSV files must have:
|
|
565
|
+
- **Required columns**: timestamp, open, high, low, close
|
|
566
|
+
- **Optional columns**: volume, any indicator columns
|
|
567
|
+
- **Timestamp format**: ISO8601, Unix timestamp, or parseable date string
|
|
568
|
+
|
|
569
|
+
**Example CSV**:
|
|
570
|
+
```csv
|
|
571
|
+
timestamp,open,high,low,close,volume
|
|
572
|
+
2024-01-01 00:00:00,100.0,102.0,99.0,101.0,1000
|
|
573
|
+
2024-01-01 00:01:00,101.0,103.0,100.5,102.0,1500
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
578
|
+
## Performance
|
|
579
|
+
|
|
580
|
+
### Benchmarks
|
|
581
|
+
|
|
582
|
+
- **Render**: 500k+ points at 60fps
|
|
583
|
+
- **Load**: 100MB CSV in <2 seconds
|
|
584
|
+
- **Interaction**: <16ms zoom/pan latency
|
|
585
|
+
- **Memory**: Efficient columnar data storage
|
|
586
|
+
|
|
587
|
+
### Optimization Tips
|
|
588
|
+
|
|
589
|
+
1. **Use appropriate timeframes**: Higher timeframes = less data = faster rendering
|
|
590
|
+
2. **Limit indicators**: Each indicator adds a render pass
|
|
591
|
+
3. **CSV optimization**: Pre-calculate indicators and save to CSV
|
|
592
|
+
4. **Batch operations**: Load multiple indicators at once instead of one-by-one
|
|
593
|
+
|
|
594
|
+
---
|
|
595
|
+
|
|
596
|
+
## Troubleshooting
|
|
597
|
+
|
|
598
|
+
### Common Issues
|
|
599
|
+
|
|
600
|
+
**"Failed to load chart"**
|
|
601
|
+
- Ensure CSV file exists in `data/` directory
|
|
602
|
+
- Check CSV format (must have OHLC columns)
|
|
603
|
+
- Verify server is running (`http://localhost:8000/health`)
|
|
604
|
+
|
|
605
|
+
**"Module not found"**
|
|
606
|
+
- Run `poetry install` to install dependencies
|
|
607
|
+
- Ensure you're using `poetry run python` or `poetry shell`
|
|
608
|
+
|
|
609
|
+
**"Port already in use"**
|
|
610
|
+
- Server auto-increments to find available port
|
|
611
|
+
- Check terminal output for actual port number
|
|
612
|
+
- Or manually kill process: `lsof -ti:8000 | xargs kill -9`
|
|
613
|
+
|
|
614
|
+
**Charts not rendering**
|
|
615
|
+
- Open browser console (F12) to check for JavaScript errors
|
|
616
|
+
- Verify data is being returned: `http://localhost:8000/chart-data?filename=sample.csv`
|
|
617
|
+
- Check that uPlot CDN is accessible
|
|
618
|
+
|
|
619
|
+
**Indicators not showing**
|
|
620
|
+
- Ensure indicator data exists in CSV or can be calculated
|
|
621
|
+
- Check browser console for errors
|
|
622
|
+
- Verify indicator names match expected format (e.g., `rsi_14`, not `RSI-14`)
|
|
623
|
+
|
|
624
|
+
---
|
|
625
|
+
|
|
626
|
+
## Additional Resources
|
|
627
|
+
|
|
628
|
+
### Sample Data
|
|
629
|
+
|
|
630
|
+
Generate custom sample data:
|
|
631
|
+
```bash
|
|
632
|
+
poetry run python scripts/generate_sample_data.py \
|
|
633
|
+
--output data/custom.csv \
|
|
634
|
+
--periods 10000 \
|
|
635
|
+
--freq 1min \
|
|
636
|
+
--price 100.0 \
|
|
637
|
+
--volatility 0.02
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### API Documentation
|
|
641
|
+
|
|
642
|
+
Interactive API docs available at:
|
|
643
|
+
- Swagger UI: `http://localhost:8000/docs`
|
|
644
|
+
- ReDoc: `http://localhost:8000/redoc`
|
|
645
|
+
|
|
646
|
+
---
|
|
647
|
+
|
|
648
|
+
## License
|
|
649
|
+
|
|
650
|
+
MIT License - see LICENSE file for details
|
|
651
|
+
|
|
652
|
+
---
|
|
653
|
+
|
|
654
|
+
## Contributing
|
|
655
|
+
|
|
656
|
+
Contributions welcome! Please:
|
|
657
|
+
1. Fork the repository
|
|
658
|
+
2. Create a feature branch
|
|
659
|
+
3. Add tests for new functionality
|
|
660
|
+
4. Ensure all tests pass
|
|
661
|
+
5. Submit a pull request
|
|
662
|
+
|
|
663
|
+
---
|
|
664
|
+
|
|
665
|
+
## Support
|
|
666
|
+
|
|
667
|
+
For issues, questions, or suggestions:
|
|
668
|
+
- Open an issue on GitHub
|
|
669
|
+
- Check existing issues for solutions
|
|
670
|
+
- Review documentation and examples
|
|
671
|
+
|
|
672
|
+
---
|
|
673
|
+
|
|
674
|
+
Built with ❤️ using FastAPI, uPlot, and Python
|
|
675
|
+
---
|
|
676
|
+
Built with ❤️ from Dubai
|