11c67b3c44
Co-authored-by: Cursor <cursoragent@cursor.com>
107 lines
3.6 KiB
Python
107 lines
3.6 KiB
Python
"""实例数据统计:复盘 KPI + 权益曲线 API,以及后台权益采样."""
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Callable
|
|
|
|
from flask import Flask, jsonify, request
|
|
|
|
|
|
def install_instance_data_stats(app: Flask, app_module: Any) -> None:
|
|
get_db: Callable = app_module.get_db
|
|
login_required = app_module.login_required
|
|
|
|
def _fetch_equity_snapshot() -> dict[str, Any]:
|
|
from lib.instance.instance_cash_sample_lib import (
|
|
cash_usdt_from_balances,
|
|
equity_usdt_from_snapshot,
|
|
)
|
|
|
|
ex = getattr(app_module, "exchange_options", None)
|
|
bal: dict[str, Any] = {}
|
|
if ex is not None:
|
|
try:
|
|
from lib.exchange.okx_options_lib import fetch_options_balances
|
|
|
|
bal = fetch_options_balances(ex, force=False) or {}
|
|
except Exception:
|
|
bal = {}
|
|
cash = cash_usdt_from_balances(bal if isinstance(bal, dict) else None)
|
|
|
|
positions: list[dict[str, Any]] = []
|
|
cfg = None
|
|
try:
|
|
cfg = app.extensions.get("options_cfg")
|
|
except Exception:
|
|
cfg = None
|
|
if isinstance(cfg, dict) and cfg.get("enabled"):
|
|
try:
|
|
from lib.options.options_dashboard_lib import (
|
|
fetch_light_option_positions_for_dashboard,
|
|
)
|
|
|
|
positions = fetch_light_option_positions_for_dashboard(cfg) or []
|
|
except Exception:
|
|
positions = []
|
|
|
|
btc_px = eth_px = None
|
|
if ex is not None:
|
|
try:
|
|
from lib.options.options_stats_lib import _underlying_index_usdt
|
|
|
|
btc_px = _underlying_index_usdt(ex, "BTC")
|
|
eth_px = _underlying_index_usdt(ex, "ETH")
|
|
except Exception:
|
|
btc_px = eth_px = None
|
|
if (btc_px is None or eth_px is None) and positions:
|
|
for p in positions:
|
|
if not isinstance(p, dict):
|
|
continue
|
|
under = str(p.get("underlying") or "").upper()
|
|
idx = None
|
|
try:
|
|
idx = float(p.get("idx_px") or p.get("index_px") or 0)
|
|
except (TypeError, ValueError):
|
|
idx = None
|
|
if idx is None or idx <= 0:
|
|
continue
|
|
if under == "BTC" and btc_px is None:
|
|
btc_px = idx
|
|
elif under == "ETH" and eth_px is None:
|
|
eth_px = idx
|
|
|
|
equity = equity_usdt_from_snapshot(
|
|
bal if isinstance(bal, dict) else None,
|
|
positions,
|
|
btc_px=btc_px,
|
|
eth_px=eth_px,
|
|
)
|
|
return {"cash_usdt": cash, "equity_usdt": equity}
|
|
|
|
@app.route("/api/instance/cash_curve")
|
|
@login_required
|
|
def api_instance_cash_curve():
|
|
from lib.instance.instance_cash_sample_lib import (
|
|
query_cash_series_with_db,
|
|
sample_once,
|
|
)
|
|
|
|
try:
|
|
sample_once(get_db, _fetch_equity_snapshot)
|
|
except Exception:
|
|
pass
|
|
range_key = (request.args.get("range") or "7d").strip()
|
|
conn = get_db()
|
|
try:
|
|
return jsonify(query_cash_series_with_db(conn, range_key=range_key))
|
|
except Exception as e:
|
|
return jsonify({"ok": False, "msg": f"加载资金曲线失败: {e}"}), 500
|
|
finally:
|
|
conn.close()
|
|
|
|
try:
|
|
from lib.instance.instance_cash_sample_lib import start_cash_sample_loop
|
|
|
|
start_cash_sample_loop(get_db, _fetch_equity_snapshot)
|
|
except Exception as e:
|
|
print(f"[instance_cash_sample] start skipped: {e}", flush=True)
|