89 lines
2.7 KiB
Python
89 lines
2.7 KiB
Python
"""Market overview snapshot persistence."""
|
|
|
|
import json
|
|
from datetime import datetime
|
|
|
|
from app.db.schema import get_conn
|
|
|
|
|
|
SNAPSHOT_TYPE = "crypto_market"
|
|
|
|
|
|
def _now():
|
|
return datetime.now().isoformat(timespec="seconds")
|
|
|
|
|
|
def save_market_snapshot(data, *, status="success", error_message=""):
|
|
payload = data if isinstance(data, dict) else {}
|
|
snapshot_time = str(payload.get("updated_at") or _now())
|
|
source = str(payload.get("source") or "binance_spot_usdt_market")
|
|
conn = get_conn()
|
|
try:
|
|
row = conn.execute(
|
|
"""
|
|
INSERT INTO market_snapshots (
|
|
snapshot_type, source, snapshot_time, data_json, status, error_message, created_at
|
|
) VALUES (%s, %s, %s, %s, %s, %s, %s)
|
|
RETURNING id
|
|
""",
|
|
(
|
|
SNAPSHOT_TYPE,
|
|
source,
|
|
snapshot_time,
|
|
json.dumps(payload, ensure_ascii=False, default=str),
|
|
status,
|
|
str(error_message or "")[:1000],
|
|
_now(),
|
|
),
|
|
).fetchone()
|
|
conn.commit()
|
|
return int(row["id"] if row else 0)
|
|
except Exception:
|
|
conn.rollback()
|
|
raise
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def get_latest_market_snapshot(max_age_seconds=None):
|
|
conn = get_conn()
|
|
try:
|
|
row = conn.execute(
|
|
"""
|
|
SELECT *
|
|
FROM market_snapshots
|
|
WHERE snapshot_type=%s AND status='success'
|
|
ORDER BY snapshot_time DESC, id DESC
|
|
LIMIT 1
|
|
""",
|
|
(SNAPSHOT_TYPE,),
|
|
).fetchone()
|
|
finally:
|
|
conn.close()
|
|
if not row:
|
|
return None
|
|
item = dict(row)
|
|
try:
|
|
data = json.loads(item.get("data_json") or "{}")
|
|
except Exception:
|
|
data = {}
|
|
if not isinstance(data, dict):
|
|
data = {}
|
|
data.setdefault("updated_at", item.get("snapshot_time") or "")
|
|
data.setdefault("source", item.get("source") or "binance_spot_usdt_market")
|
|
if max_age_seconds:
|
|
try:
|
|
ts = datetime.fromisoformat(str(item.get("snapshot_time") or data.get("updated_at")))
|
|
data["snapshot_age_seconds"] = max(0, round((datetime.now() - ts).total_seconds()))
|
|
data["snapshot_stale"] = data["snapshot_age_seconds"] > int(max_age_seconds)
|
|
except Exception:
|
|
data["snapshot_age_seconds"] = None
|
|
data["snapshot_stale"] = True
|
|
return data
|
|
|
|
|
|
def save_market_error(error_message, data=None):
|
|
payload = data if isinstance(data, dict) else {"updated_at": _now(), "source": "binance_spot_usdt_market"}
|
|
return save_market_snapshot(payload, status="error", error_message=error_message)
|
|
|