chore: deployed JSON retirement
This commit is contained in:
-40644
File diff suppressed because it is too large
Load Diff
+231
-236
@@ -1,236 +1,231 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
mo_config.py — MoFin 统一配置管理(单例模式)
|
mo_config.py — MoFin 统一配置管理(单例模式)
|
||||||
|
|
||||||
替代 MoFin 中散落在各文件的硬编码路径和常量。
|
替代 MoFin 中散落在各文件的硬编码路径和常量。
|
||||||
|
|
||||||
⚠️ 铁律:所有 MoFin 模块必须从此处获取路径和配置,严禁硬编码。
|
⚠️ 铁律:所有 MoFin 模块必须从此处获取路径和配置,严禁硬编码。
|
||||||
之前:DATA_DIR = "/home/hmo/web-dashboard/data" (散落在 10+ 文件中)
|
之前:DATA_DIR = "/home/hmo/web-dashboard/data" (散落在 10+ 文件中)
|
||||||
现在:from mo_config import config; config.data_dir
|
现在:from mo_config import config; config.data_dir
|
||||||
|
|
||||||
用法:
|
用法:
|
||||||
from mo_config import config
|
from mo_config import config
|
||||||
from mo_data import read_portfolio; data = read_portfolio()
|
from mo_data import read_portfolio; data = read_portfolio()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MoConfig:
|
class MoConfig:
|
||||||
"""MoFin 全局配置单例"""
|
"""MoFin 全局配置单例"""
|
||||||
|
|
||||||
# ── 路径 ──────────────────────────────────────────────────────
|
# ── 路径 ──────────────────────────────────────────────────────
|
||||||
# 项目根目录
|
# 项目根目录
|
||||||
project_dir: Path = field(default_factory=lambda: Path(__file__).parent.resolve())
|
project_dir: Path = field(default_factory=lambda: Path(__file__).parent.resolve())
|
||||||
|
|
||||||
# 数据目录(mofin.db 等,所有数据只从 DB 读写)
|
# 数据目录(mofin.db 等,所有数据只从 DB 读写)
|
||||||
data_dir: Path = field(default_factory=lambda: Path(
|
data_dir: Path = field(default_factory=lambda: Path(
|
||||||
os.environ.get("MOFIN_DATA_DIR", "/home/hmo/web-dashboard/data")
|
os.environ.get("MOFIN_DATA_DIR", "/home/hmo/web-dashboard/data")
|
||||||
))
|
))
|
||||||
|
|
||||||
# SQLite 数据库路径
|
# SQLite 数据库路径
|
||||||
db_path: Path = field(default=None)
|
db_path: Path = field(default=None)
|
||||||
|
|
||||||
# 缓存目录
|
# 缓存目录
|
||||||
cache_dir: Path = field(default_factory=lambda: Path.home() / ".cache" / "mofin")
|
cache_dir: Path = field(default_factory=lambda: Path.home() / ".cache" / "mofin")
|
||||||
|
|
||||||
# Hermes 状态目录
|
# Hermes 状态目录
|
||||||
hermes_dir: Path = field(default_factory=lambda: Path.home() / ".hermes")
|
hermes_dir: Path = field(default_factory=lambda: Path.home() / ".hermes")
|
||||||
|
|
||||||
# ── 关键数据文件路径(已废弃,仅保留为检查逻辑。新代码勿用) ──────
|
# ── 关键数据文件路径(已废弃,仅保留为检查逻辑。新代码勿用) ──────
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def portfolio_path(self) -> Path:
|
def portfolio_path(self) -> Path:
|
||||||
"""⚠️ DEPRECATED: 数据已迁至 mofin.db holdings + portfolio_summary 表。"""
|
"""⚠️ DEPRECATED: 数据已迁至 mofin.db holdings + portfolio_summary 表。"""
|
||||||
import warnings
|
import warnings
|
||||||
warnings.warn("portfolio_path is deprecated — use mo_data.read_portfolio() for DB data", DeprecationWarning, stacklevel=2)
|
warnings.warn("portfolio_path is deprecated — use mo_data.read_portfolio() for DB data", DeprecationWarning, stacklevel=2)
|
||||||
return Path()
|
return Path()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def decisions_path(self) -> Path:
|
def decisions_path(self) -> Path:
|
||||||
"""⚠️ DEPRECATED: 数据已迁至 mofin.db holding_strategies 表。"""
|
"""⚠️ DEPRECATED: 数据已迁至 mofin.db holding_strategies 表。"""
|
||||||
import warnings
|
import warnings
|
||||||
warnings.warn("decisions_path is deprecated — use mo_data.read_decisions() for DB data", DeprecationWarning, stacklevel=2)
|
warnings.warn("decisions_path is deprecated — use mo_data.read_decisions() for DB data", DeprecationWarning, stacklevel=2)
|
||||||
return Path()
|
return Path()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def watchlist_path(self) -> Path:
|
def watchlist_path(self) -> Path:
|
||||||
"""⚠️ DEPRECATED: 数据已迁至 mofin.db watchlist_stocks 表。"""
|
"""⚠️ DEPRECATED: 数据已迁至 mofin.db watchlist_stocks 表。"""
|
||||||
import warnings
|
import warnings
|
||||||
warnings.warn("watchlist_path is deprecated — use mo_data.read_watchlist() for DB data", DeprecationWarning, stacklevel=2)
|
warnings.warn("watchlist_path is deprecated — use mo_data.read_watchlist() for DB data", DeprecationWarning, stacklevel=2)
|
||||||
return Path()
|
return Path()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def price_events_path(self) -> Path:
|
def live_prices_path(self) -> Path:
|
||||||
"""⚠️ 已废弃!数据在 mofin.db price_events 表。"""
|
"""⚠️ DEPRECATED: 实时价格已迁移到 mofin_db.live_prices 表。"""
|
||||||
return self.data_dir / "price_events.json"
|
return self.data_dir / "live_prices.json"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def live_prices_path(self) -> Path:
|
def evaluation_input_path(self) -> Path:
|
||||||
"""⚠️ DEPRECATED: 实时价格已迁移到 mofin_db.live_prices 表。"""
|
return self.data_dir / "evaluation_input.json"
|
||||||
return self.data_dir / "live_prices.json"
|
|
||||||
|
@property
|
||||||
@property
|
def multi_tf_cache_path(self) -> Path:
|
||||||
def evaluation_input_path(self) -> Path:
|
"""⚠️ DEPRECATED: 多周期缓存已迁移到 mofin_db.mtf_cache 表。"""
|
||||||
return self.data_dir / "evaluation_input.json"
|
return self.data_dir / "multi_tf_cache.json"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def multi_tf_cache_path(self) -> Path:
|
def price_history_path(self) -> Path:
|
||||||
"""⚠️ DEPRECATED: 多周期缓存已迁移到 mofin_db.mtf_cache 表。"""
|
return self.data_dir / "price_history.json"
|
||||||
return self.data_dir / "multi_tf_cache.json"
|
|
||||||
|
# ── DB 路径(懒加载) ────────────────────────────────────────
|
||||||
@property
|
|
||||||
def price_history_path(self) -> Path:
|
def _get_db_path(self) -> Path:
|
||||||
return self.data_dir / "price_history.json"
|
if self.db_path is None:
|
||||||
|
self.db_path = self.data_dir / "mofin.db"
|
||||||
# ── DB 路径(懒加载) ────────────────────────────────────────
|
return self.db_path
|
||||||
|
|
||||||
def _get_db_path(self) -> Path:
|
# ── 汇率 ──────────────────────────────────────────────────────
|
||||||
if self.db_path is None:
|
|
||||||
self.db_path = self.data_dir / "mofin.db"
|
hk_rate_fallback: float = 0.87 # 港币→人民币 fallback 汇率
|
||||||
return self.db_path
|
|
||||||
|
# ── 小果 LLM 端点(用机器名,/etc/hosts 自动解析 LAN/EasyTier)─
|
||||||
# ── 汇率 ──────────────────────────────────────────────────────
|
# node122 = 192.168.1.122 (LAN) / 10.144.144.2 (EasyTier)
|
||||||
|
xiaoguo_host: str = "node122"
|
||||||
hk_rate_fallback: float = 0.87 # 港币→人民币 fallback 汇率
|
xiaoguo_port: int = 18003
|
||||||
|
|
||||||
# ── 小果 LLM 端点(用机器名,/etc/hosts 自动解析 LAN/EasyTier)─
|
@property
|
||||||
# node122 = 192.168.1.122 (LAN) / 10.144.144.2 (EasyTier)
|
def xiaoguo_url(self) -> str:
|
||||||
xiaoguo_host: str = "node122"
|
return f"http://{self.xiaoguo_host}:{self.xiaoguo_port}"
|
||||||
xiaoguo_port: int = 18003
|
|
||||||
|
@property
|
||||||
@property
|
def xiaoguo_api_url(self) -> str:
|
||||||
def xiaoguo_url(self) -> str:
|
return f"{self.xiaoguo_url}/v1/chat/completions"
|
||||||
return f"http://{self.xiaoguo_host}:{self.xiaoguo_port}"
|
|
||||||
|
port: int = field(default_factory=lambda: int(os.environ.get("PORT", "8899")))
|
||||||
@property
|
|
||||||
def xiaoguo_api_url(self) -> str:
|
tdx_relay_url: str = field(
|
||||||
return f"{self.xiaoguo_url}/v1/chat/completions"
|
default_factory=lambda: os.environ.get("TDX_RELAY_URL", "http://localhost:8080")
|
||||||
|
)
|
||||||
port: int = field(default_factory=lambda: int(os.environ.get("PORT", "8899")))
|
|
||||||
|
xmpp_agent_host: str = field(
|
||||||
tdx_relay_url: str = field(
|
default_factory=lambda: os.environ.get("XMPP_AGENT_HOST", "localhost")
|
||||||
default_factory=lambda: os.environ.get("TDX_RELAY_URL", "http://localhost:8080")
|
)
|
||||||
)
|
|
||||||
|
xmpp_agent_port: int = field(
|
||||||
xmpp_agent_host: str = field(
|
default_factory=lambda: int(os.environ.get("XMPP_AGENT_PORT", "5801"))
|
||||||
default_factory=lambda: os.environ.get("XMPP_AGENT_HOST", "localhost")
|
)
|
||||||
)
|
|
||||||
|
# ── DSA 集成 ──────────────────────────────────────────────────
|
||||||
xmpp_agent_port: int = field(
|
|
||||||
default_factory=lambda: int(os.environ.get("XMPP_AGENT_PORT", "5801"))
|
dsa_enabled: bool = field(
|
||||||
)
|
default_factory=lambda: os.environ.get("DSA_ENABLED", "false").lower() == "true"
|
||||||
|
)
|
||||||
# ── DSA 集成 ──────────────────────────────────────────────────
|
|
||||||
|
dsa_base_dir: Path = field(default_factory=lambda: Path(
|
||||||
dsa_enabled: bool = field(
|
os.path.normpath(os.path.join(
|
||||||
default_factory=lambda: os.environ.get("DSA_ENABLED", "false").lower() == "true"
|
os.path.dirname(os.path.abspath(__file__)),
|
||||||
)
|
"..", "daily-stock-analysis",
|
||||||
|
"ZhuLinsen-daily_stock_analysis-a448886"
|
||||||
dsa_base_dir: Path = field(default_factory=lambda: Path(
|
))
|
||||||
os.path.normpath(os.path.join(
|
))
|
||||||
os.path.dirname(os.path.abspath(__file__)),
|
|
||||||
"..", "daily-stock-analysis",
|
# ── 数据新鲜度 ────────────────────────────────────────────────
|
||||||
"ZhuLinsen-daily_stock_analysis-a448886"
|
|
||||||
))
|
market_hours_max_stale_min: int = 5 # 盘中最大过期时间(分钟)
|
||||||
))
|
off_hours_max_stale_min: int = 120 # 盘后最大过期时间(分钟)
|
||||||
|
|
||||||
# ── 数据新鲜度 ────────────────────────────────────────────────
|
# ── 验证 ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
market_hours_max_stale_min: int = 5 # 盘中最大过期时间(分钟)
|
def validate(self) -> List[str]:
|
||||||
off_hours_max_stale_min: int = 120 # 盘后最大过期时间(分钟)
|
"""验证配置,返回问题列表"""
|
||||||
|
issues = []
|
||||||
# ── 验证 ──────────────────────────────────────────────────────
|
|
||||||
|
if not self.data_dir.exists():
|
||||||
def validate(self) -> List[str]:
|
issues.append(f"数据目录不存在: {self.data_dir}")
|
||||||
"""验证配置,返回问题列表"""
|
|
||||||
issues = []
|
if not self.portfolio_path.exists():
|
||||||
|
issues.append(f"portfolio_path 不存在(已废弃): {self.portfolio_path}")
|
||||||
if not self.data_dir.exists():
|
|
||||||
issues.append(f"数据目录不存在: {self.data_dir}")
|
if not self.decisions_path.exists():
|
||||||
|
issues.append(f"decisions_path 不存在(已废弃): {self.decisions_path}")
|
||||||
if not self.portfolio_path.exists():
|
|
||||||
issues.append(f"portfolio_path 不存在(已废弃): {self.portfolio_path}")
|
return issues
|
||||||
|
|
||||||
if not self.decisions_path.exists():
|
def ensure_dirs(self):
|
||||||
issues.append(f"decisions_path 不存在(已废弃): {self.decisions_path}")
|
"""确保必要的目录存在"""
|
||||||
|
self.data_dir.mkdir(parents=True, exist_ok=True)
|
||||||
return issues
|
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
self.hermes_dir.mkdir(parents=True, exist_ok=True)
|
||||||
def ensure_dirs(self):
|
|
||||||
"""确保必要的目录存在"""
|
# ── 输出 ──────────────────────────────────────────────────────
|
||||||
self.data_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
def summary(self) -> str:
|
||||||
self.hermes_dir.mkdir(parents=True, exist_ok=True)
|
"""打印配置摘要"""
|
||||||
|
lines = [
|
||||||
# ── 输出 ──────────────────────────────────────────────────────
|
"=== MoFin 配置 ===",
|
||||||
|
f"项目目录: {self.project_dir}",
|
||||||
def summary(self) -> str:
|
f"数据目录: {self.data_dir} (存在: {self.data_dir.exists()})",
|
||||||
"""打印配置摘要"""
|
f"DB路径: {self._get_db_path()} (存在: {self._get_db_path().exists()})",
|
||||||
lines = [
|
f"端口: {self.port}",
|
||||||
"=== MoFin 配置 ===",
|
f"TDX Relay: {self.tdx_relay_url}",
|
||||||
f"项目目录: {self.project_dir}",
|
f"DSA 集成: {'启用' if self.dsa_enabled else '关闭'}",
|
||||||
f"数据目录: {self.data_dir} (存在: {self.data_dir.exists()})",
|
f"港币汇率 fallback: {self.hk_rate_fallback}",
|
||||||
f"DB路径: {self._get_db_path()} (存在: {self._get_db_path().exists()})",
|
]
|
||||||
f"端口: {self.port}",
|
issues = self.validate()
|
||||||
f"TDX Relay: {self.tdx_relay_url}",
|
if issues:
|
||||||
f"DSA 集成: {'启用' if self.dsa_enabled else '关闭'}",
|
lines.append(f"\n⚠️ 配置问题 ({len(issues)}):")
|
||||||
f"港币汇率 fallback: {self.hk_rate_fallback}",
|
for i in issues:
|
||||||
]
|
lines.append(f" - {i}")
|
||||||
issues = self.validate()
|
return "\n".join(lines)
|
||||||
if issues:
|
|
||||||
lines.append(f"\n⚠️ 配置问题 ({len(issues)}):")
|
|
||||||
for i in issues:
|
# ── 单例 ────────────────────────────────────────────────────────────
|
||||||
lines.append(f" - {i}")
|
|
||||||
return "\n".join(lines)
|
_config_instance: MoConfig | None = None
|
||||||
|
|
||||||
|
|
||||||
# ── 单例 ────────────────────────────────────────────────────────────
|
def get_config() -> MoConfig:
|
||||||
|
"""获取全局配置单例"""
|
||||||
_config_instance: MoConfig | None = None
|
global _config_instance
|
||||||
|
if _config_instance is None:
|
||||||
|
_config_instance = MoConfig()
|
||||||
def get_config() -> MoConfig:
|
return _config_instance
|
||||||
"""获取全局配置单例"""
|
|
||||||
global _config_instance
|
|
||||||
if _config_instance is None:
|
# 便捷别名
|
||||||
_config_instance = MoConfig()
|
config = property(lambda self: get_config())
|
||||||
return _config_instance
|
|
||||||
|
|
||||||
|
# ── 模块级便捷访问 ──────────────────────────────────────────────────
|
||||||
# 便捷别名
|
|
||||||
config = property(lambda self: get_config())
|
def data_dir() -> Path:
|
||||||
|
return get_config().data_dir
|
||||||
|
|
||||||
# ── 模块级便捷访问 ──────────────────────────────────────────────────
|
def ensure_dirs():
|
||||||
|
get_config().ensure_dirs()
|
||||||
def data_dir() -> Path:
|
|
||||||
return get_config().data_dir
|
|
||||||
|
# ── 向后兼容:导出已废弃的路由常量 ──────────────────────────────────
|
||||||
def ensure_dirs():
|
# PORTFOLIO_PATH / DECISIONS_PATH / WATCHLIST_PATH 均已废弃(数据在 DB)。
|
||||||
get_config().ensure_dirs()
|
|
||||||
|
def _lazy(attr):
|
||||||
|
"""懒加载属性,首次访问时从 config 获取"""
|
||||||
# ── 向后兼容:导出已废弃的路由常量 ──────────────────────────────────
|
return getattr(get_config(), attr)
|
||||||
# PORTFOLIO_PATH / DECISIONS_PATH / WATCHLIST_PATH 均已废弃(数据在 DB)。
|
|
||||||
|
# 为兼容旧代码导出以下变量
|
||||||
def _lazy(attr):
|
PORTFOLIO_PATH = None # 改用 config.portfolio_path
|
||||||
"""懒加载属性,首次访问时从 config 获取"""
|
DECISIONS_PATH = None # 改用 config.decisions_path
|
||||||
return getattr(get_config(), attr)
|
WATCHLIST_PATH = None # 改用 config.watchlist_path
|
||||||
|
|
||||||
# 为兼容旧代码导出以下变量
|
|
||||||
PORTFOLIO_PATH = None # 改用 config.portfolio_path
|
# ── 自检 ────────────────────────────────────────────────────────────
|
||||||
DECISIONS_PATH = None # 改用 config.decisions_path
|
|
||||||
WATCHLIST_PATH = None # 改用 config.watchlist_path
|
if __name__ == "__main__":
|
||||||
|
cfg = get_config()
|
||||||
|
print(cfg.summary())
|
||||||
# ── 自检 ────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
cfg = get_config()
|
|
||||||
print(cfg.summary())
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from datetime import datetime
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Callable
|
from typing import Optional, Callable
|
||||||
|
|
||||||
DATA_DIR = Path(__file__).parent / "data"
|
DATA_DIR = Path("/home/hmo/MoFin/data") # 绝对路径:全系统唯一权威数据目录
|
||||||
DB_PATH = DATA_DIR / "mofin.db"
|
DB_PATH = DATA_DIR / "mofin.db"
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,6 @@ from mo_data import read_decisions
|
|||||||
|
|
||||||
DATA_DIR = Path(__file__).parent.parent / "data"
|
DATA_DIR = Path(__file__).parent.parent / "data"
|
||||||
ACCURACY_PATH = DATA_DIR / "accuracy_stats.json"
|
ACCURACY_PATH = DATA_DIR / "accuracy_stats.json"
|
||||||
EVENTS_PATH = DATA_DIR / "price_events.json"
|
|
||||||
FEEDBACK_PATH = DATA_DIR / "strategy_feedback.json"
|
FEEDBACK_PATH = DATA_DIR / "strategy_feedback.json"
|
||||||
|
|
||||||
|
|
||||||
@@ -174,15 +173,16 @@ def generate_adjustment(decision, phase_check, accuracy_trend):
|
|||||||
|
|
||||||
def run():
|
def run():
|
||||||
decisions = read_decisions()
|
decisions = read_decisions()
|
||||||
# 优先从 SQLite 读取价格事件
|
# 价格事件:只从 DB price_events 表读(JSON 已退役)
|
||||||
try:
|
try:
|
||||||
from mofin_db import get_conn, query_price_events
|
from mofin_db import get_conn, query_price_events
|
||||||
conn = get_conn()
|
conn = get_conn()
|
||||||
pe_rows = query_price_events(conn, limit=50000)
|
pe_rows = query_price_events(conn, limit=50000)
|
||||||
conn.close()
|
conn.close()
|
||||||
events = {"events": pe_rows}
|
events = {"events": pe_rows}
|
||||||
except Exception:
|
except Exception as e:
|
||||||
events = load_json(EVENTS_PATH, {"events": []})
|
print(f"[strategy_feedback] DB价格事件读取失败: {e}", file=sys.stderr)
|
||||||
|
events = {"events": []}
|
||||||
accuracy_stats = load_json(ACCURACY_PATH, {})
|
accuracy_stats = load_json(ACCURACY_PATH, {})
|
||||||
|
|
||||||
accuracy_trend = compute_accuracy_trend(accuracy_stats)
|
accuracy_trend = compute_accuracy_trend(accuracy_stats)
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ from datetime import datetime, timedelta
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
DATA_DIR = Path("/home/hmo/web-dashboard/data")
|
DATA_DIR = Path("/home/hmo/web-dashboard/data")
|
||||||
EVENTS_PATH = DATA_DIR / "price_events.json"
|
|
||||||
EVALUATION_PATH = DATA_DIR / "evaluation.json"
|
EVALUATION_PATH = DATA_DIR / "evaluation.json"
|
||||||
ACCURACY_PATH = DATA_DIR / "accuracy_stats.json"
|
ACCURACY_PATH = DATA_DIR / "accuracy_stats.json"
|
||||||
CRON_JOBS = "/home/hmo/.hermes/profiles/position-analyst/cron/jobs.json"
|
CRON_JOBS = "/home/hmo/.hermes/profiles/position-analyst/cron/jobs.json"
|
||||||
@@ -104,10 +103,9 @@ def run():
|
|||||||
lines.append(check(False, "MoFin DB 数据读取失败"))
|
lines.append(check(False, "MoFin DB 数据读取失败"))
|
||||||
warn_count += 3
|
warn_count += 3
|
||||||
|
|
||||||
# 仍为 JSON 文件的检查
|
# 仍为 JSON 文件的检查(price_events 已迁移到 DB 表,不在此列)
|
||||||
files = {
|
files = {
|
||||||
"market.json": DATA_DIR / "market.json",
|
"market.json": DATA_DIR / "market.json",
|
||||||
"price_events.json": EVENTS_PATH,
|
|
||||||
"evaluation.json": EVALUATION_PATH,
|
"evaluation.json": EVALUATION_PATH,
|
||||||
"accuracy_stats.json": ACCURACY_PATH,
|
"accuracy_stats.json": ACCURACY_PATH,
|
||||||
}
|
}
|
||||||
@@ -121,7 +119,7 @@ def run():
|
|||||||
else:
|
else:
|
||||||
ok_count += 1
|
ok_count += 1
|
||||||
|
|
||||||
# 4. 价格事件统计
|
# 4. 价格事件统计(只读 DB price_events 表,JSON 已退役)
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append("【价格事件】")
|
lines.append("【价格事件】")
|
||||||
try:
|
try:
|
||||||
@@ -130,10 +128,12 @@ def run():
|
|||||||
ev_list = query_price_events(conn, limit=50000)
|
ev_list = query_price_events(conn, limit=50000)
|
||||||
today_events = query_price_events_by_date(conn, now.strftime("%Y-%m-%d"))
|
today_events = query_price_events_by_date(conn, now.strftime("%Y-%m-%d"))
|
||||||
conn.close()
|
conn.close()
|
||||||
except Exception:
|
except Exception as e:
|
||||||
events = load_json(EVENTS_PATH, {"events": []})
|
ev_list = []
|
||||||
ev_list = events.get("events", [])
|
today_events = []
|
||||||
today_events = [e for e in ev_list if e.get("date") == now.strftime("%Y-%m-%d")]
|
lines.append(check(False, f"DB价格事件读取失败: {str(e)[:60]}"))
|
||||||
|
issues.append(f"price_events DB读取失败: {str(e)[:60]}")
|
||||||
|
warn_count += 1
|
||||||
lines.append(check(len(ev_list) > 0, f"历史事件: {len(ev_list)}条"))
|
lines.append(check(len(ev_list) > 0, f"历史事件: {len(ev_list)}条"))
|
||||||
lines.append(check(len(today_events) > 0, f"今日事件: {len(today_events)}条"))
|
lines.append(check(len(today_events) > 0, f"今日事件: {len(today_events)}条"))
|
||||||
if len(ev_list) == 0:
|
if len(ev_list) == 0:
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ from datetime import datetime
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Callable
|
from typing import Optional, Callable
|
||||||
|
|
||||||
DATA_DIR = Path(__file__).parent / "data"
|
DATA_DIR = Path("/home/hmo/MoFin/data") # 绝对路径:全系统唯一权威数据目录
|
||||||
DB_PATH = DATA_DIR / "mofin.db"
|
DB_PATH = DATA_DIR / "mofin.db"
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ from datetime import datetime
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Callable
|
from typing import Optional, Callable
|
||||||
|
|
||||||
DATA_DIR = Path(__file__).parent / "data"
|
DATA_DIR = Path("/home/hmo/MoFin/data") # 绝对路径:全系统唯一权威数据目录
|
||||||
DB_PATH = DATA_DIR / "mofin.db"
|
DB_PATH = DATA_DIR / "mofin.db"
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════
|
||||||
|
|||||||
Reference in New Issue
Block a user