From 8b88aee61f4d57f67a8f5f6a8d77a1fcdaeaa189 Mon Sep 17 00:00:00 2001 From: DiTus Date: Wed, 29 Jul 2026 09:36:35 +0200 Subject: [PATCH] Add fallback_reference for deviation when insufficient data points Add optional min_data_points (default 100) and fallback_reference config fields to indicator definitions. When available daily data points are below min_data_points, the fallback_reference value is used as the deviation reference instead of the computed mean. Applied to ratio, price, spread, and diff_pct indicator types. Configured WTI/BRENT ratio with fallback_reference=0.96065. --- WIKI/indicators.md | 17 ++++++++++++++--- _data/indicators.json | 12 ++++++++++++ indicators.py | 28 ++++++++++++++++++++++++---- 3 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 _data/indicators.json diff --git a/WIKI/indicators.md b/WIKI/indicators.md index 8bcf9ea..1940ab3 100644 --- a/WIKI/indicators.md +++ b/WIKI/indicators.md @@ -62,14 +62,16 @@ Computes `numerator / denominator`. "numerator": "xyz:CL", "denominator": "xyz:BRENTOIL", "changes": ["1h", "1d"], - "show_deviation": true + "show_deviation": true, + "min_data_points": 100, + "fallback_reference": 0.96065 } ``` - **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 +- **Deviation**: `(current - long_avg) / long_avg * 100`, where `long_avg` is the mean of daily ratios over all available history. If fewer than `min_data_points` (default 100) daily data points exist and `fallback_reference` is set, the fallback value is used instead. ### `price` — Single Price @@ -190,7 +192,7 @@ 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 +- **Reference value**: mean of daily values over all available historical data. If fewer than `min_data_points` daily data points exist and `fallback_reference` is set, the fallback value is used instead. ## Process Architecture @@ -237,3 +239,12 @@ indicators_fetcher.py (subprocess, runs every 30s) 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. + +### Optional Deviation Config Fields + +The following optional fields control the deviation reference value: + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `min_data_points` | int | 100 | Minimum number of historical daily data points required before using the computed mean as the reference | +| `fallback_reference` | float | null | If set and available data points are below `min_data_points`, this value is used as the reference instead of the computed mean | diff --git a/_data/indicators.json b/_data/indicators.json new file mode 100644 index 0000000..211cace --- /dev/null +++ b/_data/indicators.json @@ -0,0 +1,12 @@ +{ + "wti_brent_ratio": { + "display_name": "WTI/BRENT", + "type": "ratio", + "numerator": "xyz:CL", + "denominator": "xyz:BRENTOIL", + "changes": ["1h", "1d"], + "show_deviation": true, + "min_data_points": 100, + "fallback_reference": 0.96065 + } +} diff --git a/indicators.py b/indicators.py index e495cf3..d7ef8cf 100644 --- a/indicators.py +++ b/indicators.py @@ -227,7 +227,12 @@ class IndicatorCalculator: if ind_def.get("show_deviation", False): ratios = self._get_all_ratio(num, den, "1d") if ratios: - reference = sum(ratios) / len(ratios) + min_points = ind_def.get("min_data_points", 100) + fallback_ref = ind_def.get("fallback_reference") + if len(ratios) < min_points and fallback_ref is not None: + reference = fallback_ref + else: + reference = sum(ratios) / len(ratios) deviation = self._format_change(current, reference) return {"value": current, "reference": reference, "changes": changes, "deviation": deviation} @@ -250,7 +255,12 @@ class IndicatorCalculator: if ind_def.get("show_deviation", False): closes = self._get_all_closes(coin, "1d") if closes: - reference = sum(closes) / len(closes) + min_points = ind_def.get("min_data_points", 100) + fallback_ref = ind_def.get("fallback_reference") + if len(closes) < min_points and fallback_ref is not None: + reference = fallback_ref + else: + reference = sum(closes) / len(closes) deviation = self._format_change(current, reference) return {"value": current, "reference": reference, "changes": changes, "deviation": deviation} @@ -281,7 +291,12 @@ class IndicatorCalculator: if ind_def.get("show_deviation", False): spreads = self._get_all_spread(num, den, "1d") if spreads: - reference = sum(spreads) / len(spreads) + min_points = ind_def.get("min_data_points", 100) + fallback_ref = ind_def.get("fallback_reference") + if len(spreads) < min_points and fallback_ref is not None: + reference = fallback_ref + else: + reference = sum(spreads) / len(spreads) deviation = self._format_change(current, reference) return {"value": current, "reference": reference, "changes": changes, "deviation": deviation} @@ -312,7 +327,12 @@ class IndicatorCalculator: if ind_def.get("show_deviation", False): diffs = self._get_all_diff_pct(num, den, "1d") if diffs: - reference = sum(diffs) / len(diffs) + min_points = ind_def.get("min_data_points", 100) + fallback_ref = ind_def.get("fallback_reference") + if len(diffs) < min_points and fallback_ref is not None: + reference = fallback_ref + else: + reference = sum(diffs) / len(diffs) deviation = self._format_change(current, reference) return {"value": current, "reference": reference, "changes": changes, "deviation": deviation}