"""振幅统计历史作业存储(中控 JSON).""" from __future__ import annotations import json import threading import uuid from datetime import datetime, timezone from pathlib import Path from typing import Any, Optional _LOCK = threading.Lock() _STORE_NAME = "amp_stats_history.json" _MAX_ITEMS = 80 def _store_path() -> Path: return Path(__file__).resolve().parent / _STORE_NAME def _now_iso() -> str: return datetime.now(timezone.utc).astimezone().strftime("%Y-%m-%d %H:%M:%S") def _load() -> dict[str, Any]: path = _store_path() if not path.is_file(): return {"items": []} try: data = json.loads(path.read_text(encoding="utf-8")) except Exception: return {"items": []} if not isinstance(data, dict): return {"items": []} items = data.get("items") if not isinstance(items, list): items = [] return {"items": items} def _save(data: dict[str, Any]) -> None: path = _store_path() path.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8") def list_history(*, symbol: str = "", limit: int = 50) -> list[dict[str, Any]]: with _LOCK: items = list(_load().get("items") or []) sym = (symbol or "").strip().lower() if sym: items = [x for x in items if str(x.get("symbol") or "").lower() == sym] limit = max(1, min(200, int(limit or 50))) out = [] for it in items[:limit]: out.append( { "id": it.get("id"), "created_at": it.get("created_at"), "symbol": it.get("symbol"), "symbol_label": it.get("symbol_label"), "start_hour": it.get("start_hour"), "end_hour": it.get("end_hour"), "period": it.get("period"), "price_source": it.get("price_source"), "sample_count": (it.get("summary") or {}).get("sample_count"), "max_amplitude": (it.get("summary") or {}).get("max_amplitude"), "max_amplitude_day": (it.get("summary") or {}).get("max_amplitude_day"), } ) return out def get_history(item_id: str) -> Optional[dict[str, Any]]: iid = (item_id or "").strip() if not iid: return None with _LOCK: for it in _load().get("items") or []: if str(it.get("id")) == iid: return dict(it) return None def save_history(payload: dict[str, Any]) -> dict[str, Any]: item = { "id": uuid.uuid4().hex[:12], "created_at": _now_iso(), "exchange": payload.get("exchange"), "symbol": payload.get("symbol"), "symbol_label": payload.get("symbol_label"), "start_hour": payload.get("start_hour"), "end_hour": payload.get("end_hour"), "period": payload.get("period"), "timeframe": payload.get("timeframe"), "price_source": payload.get("price_source"), "inst_id": payload.get("inst_id"), "timezone": payload.get("timezone"), "summary": payload.get("summary") or {}, "rows": payload.get("rows") or [], "missing_count": payload.get("missing_count") or 0, } with _LOCK: data = _load() items = list(data.get("items") or []) items.insert(0, item) data["items"] = items[:_MAX_ITEMS] _save(data) return item def delete_history(item_id: str) -> bool: iid = (item_id or "").strip() if not iid: return False with _LOCK: data = _load() items = list(data.get("items") or []) new_items = [x for x in items if str(x.get("id")) != iid] if len(new_items) == len(items): return False data["items"] = new_items _save(data) return True