Add indicators fetcher, rich dashboard renderer, and remove trade executor/status
This commit is contained in:
90
WIKI/dashboard_configuration.md
Normal file
90
WIKI/dashboard_configuration.md
Normal file
@ -0,0 +1,90 @@
|
||||
# Dashboard Configuration Guide
|
||||
|
||||
This guide explains how to configure which tables are displayed on the live terminal dashboard.
|
||||
|
||||
## Overview
|
||||
|
||||
The dashboard is rendered by the `DashboardRenderer` class in `dashboard.py`. It currently supports two tables:
|
||||
|
||||
| Table Key | Title | Description |
|
||||
|-----------|-------|-------------|
|
||||
| `market` | Market Dashboard | Live prices, best bid/ask, gap, and direction for watched coins |
|
||||
| `strategies` | Strategies | Signal, signal price, last change, timeframe, and size for each enabled strategy |
|
||||
|
||||
Each table can be independently enabled or disabled. When only one table is visible, it takes the full terminal width. When both are visible, they split side-by-side.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Default Visibility
|
||||
|
||||
The default table visibility is set when `DashboardRenderer` is instantiated in `main_app.py` (`MainApp.__init__`):
|
||||
|
||||
```python
|
||||
self.renderer = DashboardRenderer(table_visibility={
|
||||
"market": True,
|
||||
"strategies": False,
|
||||
})
|
||||
```
|
||||
|
||||
By default, the **market table is enabled** and the **strategies table is disabled**.
|
||||
|
||||
### Changing Default Visibility
|
||||
|
||||
To change which tables are shown by default, edit the `table_visibility` dict in `main_app.py` (`MainApp.__init__`, line 349):
|
||||
|
||||
```python
|
||||
self.renderer = DashboardRenderer(table_visibility={
|
||||
"market": True,
|
||||
"strategies": True, # enable strategies table
|
||||
})
|
||||
```
|
||||
|
||||
### Runtime Toggling
|
||||
|
||||
Tables can be toggled at runtime through the `MainApp.toggle_table()` method, which delegates to `DashboardRenderer.toggle_table()`:
|
||||
|
||||
```python
|
||||
# Flip the strategies table on/off
|
||||
app.toggle_table("strategies")
|
||||
|
||||
# Explicitly enable
|
||||
app.toggle_table("strategies", enabled=True)
|
||||
|
||||
# Explicitly disable
|
||||
app.toggle_table("strategies", enabled=False)
|
||||
```
|
||||
|
||||
The same methods are available directly on the renderer:
|
||||
|
||||
```python
|
||||
renderer = DashboardRenderer()
|
||||
renderer.toggle_table("market") # flip
|
||||
renderer.toggle_table("strategies", False) # disable
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### DashboardRenderer (`dashboard.py`)
|
||||
|
||||
- `__init__(console=None, table_visibility=None)` — accepts an optional `table_visibility` dict. If not provided, defaults to `{"market": True, "strategies": False}`.
|
||||
- `toggle_table(table_name, enabled=None)` — flips the visibility state when `enabled` is `None`, or sets it to the given boolean. Raises `ValueError` for unknown table names.
|
||||
- `build_layout(...)` — conditionally builds only the tables that are enabled, then arranges them:
|
||||
- **One table:** `Layout(table)` — full width
|
||||
- **Two tables:** `Layout.split_row(Layout(t1), Layout(t2))` — side-by-side
|
||||
- **Zero tables:** empty `Layout`
|
||||
|
||||
### MainApp (`main_app.py`)
|
||||
|
||||
- `MainApp.__init__` creates the `DashboardRenderer` with the `table_visibility` config.
|
||||
- `MainApp.toggle_table(table_name, enabled=None)` delegates to the renderer for runtime toggling.
|
||||
- `MainApp.display_dashboard()` calls `renderer.build_layout()` which respects the current visibility settings.
|
||||
|
||||
## File Reference
|
||||
|
||||
| File | Line | Description |
|
||||
|------|------|-------------|
|
||||
| `dashboard.py` | 22 | `DashboardRenderer.__init__` — accepts `table_visibility` parameter |
|
||||
| `dashboard.py` | 32 | `toggle_table()` method — flips or sets table visibility |
|
||||
| `dashboard.py` | 172 | `build_layout()` — conditionally includes tables based on visibility |
|
||||
| `main_app.py` | 349 | `MainApp.__init__` — sets default `table_visibility` |
|
||||
| `main_app.py` | 385 | `MainApp.toggle_table()` — runtime toggle method |
|
||||
239
WIKI/indicators.md
Normal file
239
WIKI/indicators.md
Normal file
@ -0,0 +1,239 @@
|
||||
# Indicators Guide
|
||||
|
||||
This guide explains how to configure and use the Indicators table on the live terminal dashboard.
|
||||
|
||||
## Overview
|
||||
|
||||
The Indicators table displays computed financial indicators (e.g., WTI/BRENT ratio, live prices, moving averages, RSI) with their current value, 1-hour and 1-day percentage changes, and deviation from a long-term average.
|
||||
|
||||
The system is **config-driven** — new indicators are added by editing `_data/indicators.json`. No code changes are required for standard indicator types.
|
||||
|
||||
## Dashboard Table
|
||||
|
||||
The Indicators table is displayed below the Market table in the dashboard. It shows:
|
||||
|
||||
| Column | Description |
|
||||
|--------|-------------|
|
||||
| `#` | Indicator number |
|
||||
| `Indicator` | Display name from config |
|
||||
| `Value` | Current indicator value |
|
||||
| `1h Change` | Percentage change over the last 1 hour |
|
||||
| `1D Change` | Percentage change over the last 1 day |
|
||||
| `Deviation` | Deviation from the long-term average |
|
||||
|
||||
Changes are color-coded: **green** for positive, **red** for negative, **yellow** for neutral.
|
||||
|
||||
## Configuration
|
||||
|
||||
### Default Visibility
|
||||
|
||||
The Indicators table is enabled by default. The visibility is set in `main_app.py` (`MainApp.__init__`):
|
||||
|
||||
```python
|
||||
self.renderer = DashboardRenderer(table_visibility={
|
||||
"market": True,
|
||||
"strategies": False,
|
||||
"indicators": True,
|
||||
})
|
||||
```
|
||||
|
||||
### Runtime Toggling
|
||||
|
||||
Toggle the Indicators table at runtime:
|
||||
|
||||
```python
|
||||
app.toggle_table("indicators") # flip on/off
|
||||
app.toggle_table("indicators", enabled=True) # explicitly enable
|
||||
app.toggle_table("indicators", enabled=False) # explicitly disable
|
||||
```
|
||||
|
||||
## Indicator Types
|
||||
|
||||
The following indicator types are supported in `_data/indicators.json`:
|
||||
|
||||
### `ratio` — A/B Ratio
|
||||
|
||||
Computes `numerator / denominator`.
|
||||
|
||||
```json
|
||||
"wti_brent_ratio": {
|
||||
"display_name": "WTI/BRENT",
|
||||
"type": "ratio",
|
||||
"numerator": "xyz:CL",
|
||||
"denominator": "xyz:BRENTOIL",
|
||||
"changes": ["1h", "1d"],
|
||||
"show_deviation": true
|
||||
}
|
||||
```
|
||||
|
||||
- **Value**: `live(numerator) / live(denominator)` from latest 1m candle closes
|
||||
- **1h Change**: compares to ratio from 1h candle close prices
|
||||
- **1D Change**: compares to ratio from 1d candle close prices
|
||||
- **Deviation**: `(current - long_avg) / long_avg * 100`, where `long_avg` is the mean of daily ratios over all available history
|
||||
|
||||
### `price` — Single Price
|
||||
|
||||
```json
|
||||
"wti_price": {
|
||||
"display_name": "WTI",
|
||||
"type": "price",
|
||||
"coin": "xyz:CL",
|
||||
"changes": ["1h", "1d"],
|
||||
"show_deviation": true
|
||||
}
|
||||
```
|
||||
|
||||
- **Value**: latest close price from `{coin}_1m` table
|
||||
- **1h/1D Change**: compares to close from 1h/1d candle tables
|
||||
- **Deviation**: `(current - long_avg) / long_avg * 100`, where `long_avg` is the mean of daily closes
|
||||
|
||||
### `spread` — Price Difference
|
||||
|
||||
Computes `numerator - denominator`.
|
||||
|
||||
```json
|
||||
"wti_brent_spread": {
|
||||
"display_name": "WTI-BRENT Spread",
|
||||
"type": "spread",
|
||||
"numerator": "xyz:CL",
|
||||
"denominator": "xyz:BRENTOIL",
|
||||
"changes": ["1h", "1d"],
|
||||
"show_deviation": true
|
||||
}
|
||||
```
|
||||
|
||||
### `diff_pct` — Percentage Difference
|
||||
|
||||
Computes `(numerator - denominator) / denominator * 100`.
|
||||
|
||||
```json
|
||||
"wti_brent_diff": {
|
||||
"display_name": "WTI-BRENT Diff%",
|
||||
"type": "diff_pct",
|
||||
"numerator": "xyz:CL",
|
||||
"denominator": "xyz:BRENTOIL",
|
||||
"changes": ["1h", "1d"],
|
||||
"show_deviation": true
|
||||
}
|
||||
```
|
||||
|
||||
### `ma` — Moving Average
|
||||
|
||||
```json
|
||||
"wti_ma_20": {
|
||||
"display_name": "WTI MA(20)",
|
||||
"type": "ma",
|
||||
"coin": "xyz:CL",
|
||||
"timeframe": "1h",
|
||||
"period": 20,
|
||||
"changes": ["1h", "1d"],
|
||||
"show_deviation": true
|
||||
}
|
||||
```
|
||||
|
||||
- **Value**: latest SMA value on the specified timeframe
|
||||
- **1h Change**: compares to MA value from 1h candle table
|
||||
- **1D Change**: compares to MA value from 1d candle table
|
||||
- **Deviation**: `(current_price - MA) / MA * 100` (how far the live price is from the MA)
|
||||
|
||||
### `rsi` — Relative Strength Index
|
||||
|
||||
```json
|
||||
"wti_rsi_14": {
|
||||
"display_name": "WTI RSI(14)",
|
||||
"type": "rsi",
|
||||
"coin": "xyz:CL",
|
||||
"timeframe": "1h",
|
||||
"period": 14,
|
||||
"changes": ["1h", "1d"],
|
||||
"show_deviation": true
|
||||
}
|
||||
```
|
||||
|
||||
- **Value**: latest RSI value (0-100) on the specified timeframe
|
||||
- **1h/1D Change**: absolute change in RSI points
|
||||
- **Deviation**: `RSI - 50` (deviation from neutral)
|
||||
|
||||
### `custom` — Custom Function
|
||||
|
||||
Calls a user-defined Python function.
|
||||
|
||||
```json
|
||||
"custom_indicator": {
|
||||
"display_name": "My Custom Indicator",
|
||||
"type": "custom",
|
||||
"module": "indicators.custom_indicators",
|
||||
"function": "my_custom_calc",
|
||||
"args": {"param1": "value1"},
|
||||
"changes": ["1h", "1d"],
|
||||
"show_deviation": true
|
||||
}
|
||||
```
|
||||
|
||||
The custom function must accept `db_path` as the first argument, plus any `args` from the config, and return a dict:
|
||||
|
||||
```python
|
||||
def my_custom_calc(db_path, **kwargs):
|
||||
return {
|
||||
"value": 0.96611,
|
||||
"reference": 0.96044,
|
||||
"changes": {"1h": 0.12, "1d": -0.45},
|
||||
"deviation": 0.59
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Data Sources
|
||||
|
||||
All indicator calculations read from the SQLite database `_data/market_data.db`:
|
||||
|
||||
- **Live value**: latest close price from `{coin}_1m` candle table (updated in real-time by `live_candle_fetcher.py`)
|
||||
- **1h change**: close price from `{coin}_1h` candle table (second-to-last completed 1h candle)
|
||||
- **1D change**: close price from `{coin}_1d` candle table (second-to-last completed 1d candle)
|
||||
- **Reference value**: mean of daily values over all available historical data
|
||||
|
||||
## Process Architecture
|
||||
|
||||
```
|
||||
indicators_fetcher.py (subprocess, runs every 30s)
|
||||
|
|
||||
+---> indicators.py (IndicatorCalculator)
|
||||
| |
|
||||
| +---> _data/market_data.db (SQLite candle data)
|
||||
| +---> _data/indicators.json (config)
|
||||
|
|
||||
+---> _logs/indicators_status.json (output)
|
||||
|
|
||||
+---> main_app.py (MainApp.read_indicators_status)
|
||||
|
|
||||
+---> dashboard.py (DashboardRenderer.build_indicators_table)
|
||||
```
|
||||
|
||||
## File Reference
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `_data/indicators.json` | Indicator definitions (config) |
|
||||
| `indicators.py` | `IndicatorCalculator` class — computation logic |
|
||||
| `indicators_fetcher.py` | Standalone script — runs in a loop, computes indicators, writes JSON |
|
||||
| `dashboard.py` | `DashboardRenderer.build_indicators_table()` — renders the table |
|
||||
| `main_app.py` | `run_indicators_fetcher()` — process target; `MainApp.read_indicators_status()` — reads JSON |
|
||||
| `_logs/indicators_status.json` | Output file with computed indicator values |
|
||||
|
||||
## Adding a New Indicator
|
||||
|
||||
1. Edit `_data/indicators.json` and add a new entry:
|
||||
|
||||
```json
|
||||
"my_new_indicator": {
|
||||
"display_name": "My Indicator",
|
||||
"type": "price",
|
||||
"coin": "BTC",
|
||||
"changes": ["1h", "1d"],
|
||||
"show_deviation": true
|
||||
}
|
||||
```
|
||||
|
||||
2. Restart the application (`python main_app.py`). The Indicators Fetcher will automatically pick up the new config on its next run.
|
||||
|
||||
No code changes are needed for standard indicator types (`ratio`, `price`, `spread`, `diff_pct`, `ma`, `rsi`). For custom calculations, use the `custom` type.
|
||||
Reference in New Issue
Block a user