feat: 量化引擎加固 — 新增测试 + 数据/因子/回测层优化

- 新增 finance/tests/ 6 个测试套件(agents/backtest/dao_upsert/factors/features/fundamental_lookahead)
- 数据层: data_manager / dao 优化,新增 upsert 逻辑
- 因子层: 基本面因子抽象定位 _mapping、ROE/PE/PB 重构
- 回测层: vectorbt/engine 大改动(251 行),report 增强
- ML 层: features/backtest_integration 特征工程与回测优化
- CLI: agent_cli 重构
- config/settings 扩充配置项
This commit is contained in:
Simon
2026-08-31 14:01:06 +08:00
parent 6acf938caf
commit 73d191b43a
28 changed files with 1418 additions and 373 deletions
+69
View File
@@ -0,0 +1,69 @@
"""
DAO 写库回归测试:验证 upsert(INSERT ... ON DUPLICATE KEY UPDATE)而非 pandas replace,
确保不 DROP 表、不丢失数据、DELETE+INSERT 不再分事务。
不使用真实 DB(用假 engine 捕获 SQL)。
"""
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import pandas as pd
import pytest
from unittest.mock import patch
from database import dao
from database.models import StockDaily
from sqlalchemy.dialects import mysql
class _RecConn:
"""记录最近一次执行的 SQL。"""
def __init__(self):
self.sql = None
def begin(self):
return self
def __enter__(self):
return self
def __exit__(self, *a):
pass
def execute(self, stmt):
self.sql = str(stmt.compile(dialect=mysql.dialect(), compile_kwargs={"literal_binds": True}))
return type("R", (), {"rowcount": 2})()
class _RecEngine:
def __init__(self):
self.conn = _RecConn()
def begin(self):
return self.conn
def _daily_df():
return pd.DataFrame({
'ts_code': ['000001.SZ', '600519.SH'],
'trade_date': ['20260601', '20260601'],
'open': [10.1, 99.0], 'high': [10.8, 101.0], 'low': [10.0, 98.0],
'close': [10.5, 100.0], 'pre_close': [10.2, 99.5],
'change': [0.3, 0.5], 'pct_chg': [2.94, 0.50], 'vol': [100, 200],
'amount': [1050, 19900], 'turnover_rate': [0.5, 0.2],
})
def test_save_daily_generates_upsert_not_replace():
eng = _RecEngine()
with patch('database.dao.get_engine', return_value=eng):
dao.save_daily(_daily_df())
sql = eng.conn.sql
assert "INSERT INTO" in sql and "mac_stock_daily" in sql
assert "ON DUPLICATE KEY UPDATE" in sql, "save_daily 必须用 upsert,避免旧版 DELETE+INSERT"
# 确保不会走 pandas replace(整表重建)
assert "DROP TABLE" not in sql.upper()
def test_save_daily_no_multi_transaction():
"""save_daily 只构造一次 upsert 语句,DELETE 与 INSERT 合并为原子语句。"""
eng = _RecEngine()
with patch('database.dao.get_engine', return_value=eng):
dao.save_daily(_daily_df())
# 生成的 SQL 不含独立 DELETE,确保无"删除已发生但插入失败"的非原子风险
assert "DELETE" not in eng.conn.sql.upper()