Files
hyper/indicators_fetcher.py
DiTus 7d702e9cbd Migrate data pipeline from SQLite to PostgreSQL + Docker setup
- Add db.py PostgreSQL abstraction layer (connection, upsert, table mgmt)
- Replace sqlite3 with psycopg2 in: live_candle_fetcher, resampler,
  data_fetcher, fetch_history, import_csv, indicators, base_strategy
- Sanitize table names (colons -> underscores) for PostgreSQL compat
- Replace INSERT OR REPLACE with ON CONFLICT upserts
- Replace pandas to_sql() with batch upsert_candles()
- Add scripts: resampler_loop, gap_detector, backup_runner, cron_scheduler
- Add migrate_sqlite_to_pg.py for one-time data migration
- Add Dockerfile, docker-compose.yml, supervisord.conf
- Add postgres/postgresql.conf tuned for 4GB RAM (Synology DS1513+)
- Add .dockerignore, .env.docker.example, secrets template
- Update requirements.txt (psycopg2-binary), .gitignore
- Add MIGRATION_PLAN.md with full plan and todo list
2026-07-30 22:14:31 +02:00

87 lines
3.0 KiB
Python

"""
Indicators Data Fetcher
A standalone process that runs in a loop to compute financial indicators
(ratios, prices, MAs, RSI, custom) from SQLite candle data and save
the results to a JSON status file for the main dashboard to display.
Follows the same pattern as dashboard_data_fetcher.py.
"""
import logging
import os
import sys
import json
import time
import argparse
from datetime import datetime, timezone
from logging_utils import setup_logging
from indicators import IndicatorCalculator
class IndicatorsFetcher:
"""
Periodically computes all configured indicators and saves them to a JSON file.
"""
def __init__(self, log_level: str):
setup_logging(log_level, 'IndicatorsFetcher')
project_root = os.path.dirname(os.path.abspath(__file__))
self.db_path = os.environ.get("PG_CONN_STR", "postgresql://hyper:hyper@localhost:5432/hyper")
self.config_path = os.path.join(project_root, "_data", "indicators.json")
self.status_file_path = os.path.join(project_root, "_logs", "indicators_status.json")
self.calculator = IndicatorCalculator(
config_path=self.config_path,
db_path=self.db_path
)
logging.info(f"Indicators Fetcher initialized. DB: {self.db_path}, Config: {self.config_path}")
def fetch_and_save_indicators(self):
"""Compute all indicators and save to JSON status file."""
try:
results = self.calculator.calculate_all()
status = {
"last_updated_utc": datetime.now(timezone.utc).isoformat(),
"indicators": results
}
logs_dir = os.path.dirname(self.status_file_path)
os.makedirs(logs_dir, exist_ok=True)
temp_file_path = self.status_file_path + ".tmp"
with open(temp_file_path, 'w', encoding='utf-8') as f:
json.dump(status, f, indent=4, default=str)
os.replace(temp_file_path, self.status_file_path)
logging.debug(f"Successfully updated indicators status file with {len(results)} indicators.")
except Exception as e:
logging.error(f"Failed to fetch or save indicators: {e}", exc_info=True)
def run(self):
"""Main loop to periodically compute and save indicators."""
logging.info("Starting Indicators Fetcher loop (update interval: 30s)")
while True:
try:
self.fetch_and_save_indicators()
except Exception as e:
logging.error(f"Indicators Fetcher loop error: {e}", exc_info=True)
time.sleep(30)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Run the Indicators Data Fetcher.")
parser.add_argument("--log-level", default="normal", choices=['off', 'normal', 'debug'])
args = parser.parse_args()
fetcher = IndicatorsFetcher(log_level=args.log_level)
try:
fetcher.run()
except KeyboardInterrupt:
logging.info("Indicators Data Fetcher stopped.")