mirror of
https://ghfast.top/https://github.com/aeroxw/easy-tdx.git
synced 2026-09-12 16:54:17 +08:00
经三轮代码审计后的综合质量加固版本,覆盖协议核心层、数据正确性、 错误处理、测试真实度与可维护性。761 单测全绿(+58),ruff/mypy 全过。 主要修复: - 离线 .day 写入原子化(fsync + _repair_tail + 读取校验,CQS 守住) - 回测止损前视偏差(延迟下一根开盘 + 跳空保护) - VWAP 权重索引 / bar_time fail-fast / 绩效除零保护 - 闭包绑定 / 路径穿越 / naive datetime 跨时区 / ruff UP038 重构: - 抽 AsyncHeartbeatMixin 收敛 4 处心跳副本(12→1) - 统一 _RETRY_DELAYS 退避序列 / scanner 失败可观测性 新增 5 个测试文件 + 公共 API 类型契约,CI 加 Windows 矩阵 + trusted publishing 签名 + 锁文件。 详见 CHANGELOG.md
285 lines
7.8 KiB
Python
285 lines
7.8 KiB
Python
"""集中管理服务器地址、端口、超时等配置。
|
||
|
||
优先级:环境变量 > ~/.easy_tdx/config.json > 源码内嵌默认值。
|
||
|
||
配置文件示例::
|
||
|
||
{
|
||
"best_host": "180.153.18.170",
|
||
"best_host_updated_at": "2026-05-22T10:30:00",
|
||
"known_hosts": ["111.229.247.189", ...],
|
||
"calc_hosts": ["120.76.152.87"],
|
||
"mac_hosts": ["121.36.248.138", ...],
|
||
"port": 7709,
|
||
"timeout": 15.0
|
||
}
|
||
|
||
环境变量覆盖::
|
||
|
||
EASY_TDX_HOST -- 单台主机地址
|
||
EASY_TDX_PORT -- 端口
|
||
EASY_TDX_TIMEOUT -- 超时秒数
|
||
EASY_TDX_KNOWN_HOSTS -- 逗号分隔的候选主机列表
|
||
EASY_TDX_CONFIG_DIR -- 配置文件目录(默认 ~/.easy_tdx)
|
||
"""
|
||
|
||
import json
|
||
import os
|
||
from datetime import datetime
|
||
from pathlib import Path
|
||
from typing import Any, cast
|
||
from zoneinfo import ZoneInfo
|
||
|
||
_CONFIG_DIR = Path(os.environ.get("EASY_TDX_CONFIG_DIR", str(Path.home() / ".easy_tdx")))
|
||
_CONFIG_FILE = _CONFIG_DIR / "config.json"
|
||
|
||
# 业务时间统一用上海时区(与 client.py 一致),避免 naive datetime 跨时区歧义(审计 #18)。
|
||
_SHANGHAI_TZ = ZoneInfo("Asia/Shanghai")
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 源码内嵌默认值(config.json 不存在或字段缺失时的兜底)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
_FALLBACK_HOSTS: list[str] = [
|
||
"111.229.247.189",
|
||
"150.158.160.2",
|
||
"180.153.18.170",
|
||
"124.71.187.122",
|
||
"180.153.18.171",
|
||
"180.153.18.172",
|
||
"119.147.212.81",
|
||
"115.238.56.198",
|
||
"115.238.90.165",
|
||
"218.75.126.9",
|
||
"47.107.75.159",
|
||
"59.175.238.38",
|
||
"110.41.147.114",
|
||
"110.41.2.72",
|
||
"101.33.225.16",
|
||
"175.178.112.197",
|
||
"175.178.128.227",
|
||
"43.139.95.83",
|
||
"124.223.163.242",
|
||
"122.51.120.217",
|
||
"123.60.164.122",
|
||
"124.70.199.56",
|
||
"62.234.50.143",
|
||
"81.70.151.186",
|
||
"82.156.214.79",
|
||
"159.75.29.111",
|
||
"43.139.18.171",
|
||
"81.71.32.47",
|
||
"122.51.232.182",
|
||
"118.25.98.114",
|
||
"121.36.225.169",
|
||
"123.60.70.228",
|
||
"123.60.73.44",
|
||
"124.70.133.119",
|
||
"124.71.187.72",
|
||
"119.97.185.59",
|
||
"129.204.230.128",
|
||
"101.42.240.54",
|
||
"124.71.9.153",
|
||
"123.60.84.66",
|
||
"111.230.186.52",
|
||
"101.43.159.194",
|
||
"120.53.8.251",
|
||
"152.136.191.169",
|
||
"116.205.163.254",
|
||
"116.205.171.132",
|
||
"116.205.183.150",
|
||
"49.232.15.141",
|
||
"82.156.174.84",
|
||
"101.42.164.241",
|
||
"101.35.121.35",
|
||
"111.231.113.208",
|
||
]
|
||
|
||
_FALLBACK_CALC_HOSTS: list[str] = [
|
||
"120.76.152.87",
|
||
]
|
||
|
||
_FALLBACK_MAC_HOSTS: list[str] = [
|
||
"121.36.248.138",
|
||
"123.60.47.136",
|
||
"121.37.207.165",
|
||
]
|
||
|
||
_FALLBACK_EX_HOSTS: list[str] = [
|
||
"112.74.214.43",
|
||
"120.25.218.6",
|
||
"43.139.173.246",
|
||
"159.75.90.107",
|
||
"106.52.170.195",
|
||
"139.9.191.175",
|
||
"175.24.47.69",
|
||
"150.158.9.199",
|
||
"150.158.20.127",
|
||
"49.235.119.116",
|
||
"49.234.13.160",
|
||
"116.205.143.214",
|
||
"124.71.223.19",
|
||
"113.45.175.47",
|
||
"123.60.173.210",
|
||
"118.89.69.202",
|
||
]
|
||
|
||
_FALLBACK_MAC_EX_HOSTS: list[str] = [
|
||
"116.205.135.205",
|
||
"121.37.232.167",
|
||
]
|
||
|
||
_FALLBACK_PORT = 7709
|
||
_FALLBACK_TIMEOUT = 15.0
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 内部读写
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
def _load() -> dict[str, Any]:
|
||
try:
|
||
if _CONFIG_FILE.exists():
|
||
return cast(dict[str, Any], json.loads(_CONFIG_FILE.read_text("utf-8")))
|
||
except Exception:
|
||
pass
|
||
return {}
|
||
|
||
|
||
def _save(data: dict[str, Any]) -> None:
|
||
_CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
||
tmp = _CONFIG_FILE.with_suffix(".tmp")
|
||
tmp.write_text(json.dumps(data, indent=2, ensure_ascii=False), "utf-8")
|
||
tmp.replace(_CONFIG_FILE)
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 公开 getter
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
def get_best_host() -> str:
|
||
"""返回当前最佳主机地址。优先级:环境变量 > config.json > 默认列表首个。"""
|
||
env = os.environ.get("EASY_TDX_HOST")
|
||
if env:
|
||
return env
|
||
cfg = _load()
|
||
return cast(str, cfg.get("best_host", _FALLBACK_HOSTS[0]))
|
||
|
||
|
||
def get_known_hosts() -> list[str]:
|
||
"""返回候选行情主机列表。"""
|
||
env = os.environ.get("EASY_TDX_KNOWN_HOSTS")
|
||
if env:
|
||
return [h.strip() for h in env.split(",") if h.strip()]
|
||
cfg = _load()
|
||
return cast(list[str], cfg.get("known_hosts", list(_FALLBACK_HOSTS)))
|
||
|
||
|
||
def get_calc_hosts() -> list[str]:
|
||
"""返回计算服务器列表。"""
|
||
cfg = _load()
|
||
return cast(list[str], cfg.get("calc_hosts", list(_FALLBACK_CALC_HOSTS)))
|
||
|
||
|
||
def get_mac_hosts() -> list[str]:
|
||
"""返回 MAC 行情服务器列表。"""
|
||
cfg = _load()
|
||
return cast(list[str], cfg.get("mac_hosts", list(_FALLBACK_MAC_HOSTS)))
|
||
|
||
|
||
def get_ex_hosts() -> list[str]:
|
||
"""返回扩展行情服务器列表。"""
|
||
cfg = _load()
|
||
return cast(list[str], cfg.get("ex_hosts", list(_FALLBACK_EX_HOSTS)))
|
||
|
||
|
||
def get_best_ex_host() -> str:
|
||
"""返回当前最佳扩展行情主机。"""
|
||
env = os.environ.get("EASY_TDX_EX_HOST")
|
||
if env:
|
||
return env
|
||
cfg = _load()
|
||
return cast(str, cfg.get("best_ex_host", _FALLBACK_EX_HOSTS[0]))
|
||
|
||
|
||
def get_mac_ex_hosts() -> list[str]:
|
||
"""返回 MAC 协议扩展行情服务器列表。"""
|
||
cfg = _load()
|
||
return cast(list[str], cfg.get("mac_ex_hosts", list(_FALLBACK_MAC_EX_HOSTS)))
|
||
|
||
|
||
def get_best_mac_ex_host() -> str:
|
||
"""返回当前最佳 MAC 协议扩展行情主机。"""
|
||
env = os.environ.get("EASY_TDX_MAC_EX_HOST")
|
||
if env:
|
||
return env
|
||
cfg = _load()
|
||
return cast(str, cfg.get("best_mac_ex_host", _FALLBACK_MAC_EX_HOSTS[0]))
|
||
|
||
|
||
def get_port() -> int:
|
||
"""返回默认端口。"""
|
||
env = os.environ.get("EASY_TDX_PORT")
|
||
if env:
|
||
return int(env)
|
||
cfg = _load()
|
||
return cast(int, cfg.get("port", _FALLBACK_PORT))
|
||
|
||
|
||
def get_timeout() -> float:
|
||
"""返回默认超时秒数。"""
|
||
env = os.environ.get("EASY_TDX_TIMEOUT")
|
||
if env:
|
||
return float(env)
|
||
cfg = _load()
|
||
return cast(float, cfg.get("timeout", _FALLBACK_TIMEOUT))
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 持久化
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
def save_best_host(host: str) -> None:
|
||
"""保存最佳主机到配置文件;首次写入时同时补全默认配置。"""
|
||
cfg = _load()
|
||
cfg["best_host"] = host
|
||
cfg["best_host_updated_at"] = datetime.now(_SHANGHAI_TZ).isoformat()
|
||
if "known_hosts" not in cfg:
|
||
cfg["known_hosts"] = list(_FALLBACK_HOSTS)
|
||
if "calc_hosts" not in cfg:
|
||
cfg["calc_hosts"] = list(_FALLBACK_CALC_HOSTS)
|
||
if "mac_hosts" not in cfg:
|
||
cfg["mac_hosts"] = list(_FALLBACK_MAC_HOSTS)
|
||
if "port" not in cfg:
|
||
cfg["port"] = _FALLBACK_PORT
|
||
if "ex_hosts" not in cfg:
|
||
cfg["ex_hosts"] = list(_FALLBACK_EX_HOSTS)
|
||
if "mac_ex_hosts" not in cfg:
|
||
cfg["mac_ex_hosts"] = list(_FALLBACK_MAC_EX_HOSTS)
|
||
_save(cfg)
|
||
|
||
|
||
def save_best_ex_host(host: str) -> None:
|
||
"""保存最佳扩展行情主机到配置文件。"""
|
||
cfg = _load()
|
||
cfg["best_ex_host"] = host
|
||
cfg["best_ex_host_updated_at"] = datetime.now().isoformat()
|
||
if "ex_hosts" not in cfg:
|
||
cfg["ex_hosts"] = list(_FALLBACK_EX_HOSTS)
|
||
if "mac_ex_hosts" not in cfg:
|
||
cfg["mac_ex_hosts"] = list(_FALLBACK_MAC_EX_HOSTS)
|
||
_save(cfg)
|
||
|
||
|
||
def save_best_mac_ex_host(host: str) -> None:
|
||
"""保存最佳 MAC 协议扩展行情主机到配置文件。"""
|
||
cfg = _load()
|
||
cfg["best_mac_ex_host"] = host
|
||
cfg["best_mac_ex_host_updated_at"] = datetime.now().isoformat()
|
||
if "mac_ex_hosts" not in cfg:
|
||
cfg["mac_ex_hosts"] = list(_FALLBACK_MAC_EX_HOSTS)
|
||
_save(cfg)
|