fix(types): 修复 CI mypy strict + ruff format 失败

mypy (13 errors → 0):
- portfolio/optimizer: register_optimizer 返回类型改为 Callable 装饰器签名
  (原标注 type[WeightOptimizer] 导致 4 个子类 Too many arguments)
- factor/engine: _datetime_to_int 用 isinstance 收窄替代 object→int 强转
- factor/analysis: 删多余 type:ignore(改由 mypy override 统一处理 scipy)
- backtest/orders, execution: np.sqrt 表达式用 float() 包裹消除 no-any-return
- MyTT.pyi: MACD 签名删除错误的 LOW/HIGH 参数(与 MyTT.py 实际签名对齐)
- pyproject: 新增 scipy mypy override (ignore_missing_imports)

ruff format: 8 个 test 文件格式化

验证: 564 passed, mypy 192 文件零错误, ruff check/format 全绿
This commit is contained in:
Justin Gu
2026-06-13 21:21:33 +08:00
parent 88638e82ad
commit 5fc398255d
15 changed files with 94 additions and 54 deletions
+1
View File
@@ -1,5 +1,6 @@
# tests/unit/test_factor_analysis.py
"""Test FactorAnalyzer and FactorReport."""
from __future__ import annotations
import numpy as np
+2
View File
@@ -1,5 +1,6 @@
# tests/unit/test_factor_base.py
"""Test Factor base class and registry."""
from __future__ import annotations
import pandas as pd
@@ -97,6 +98,7 @@ class TestRegistry:
return df["close"]
with pytest.raises(ValueError, match="已注册"):
@register_factor
class Dup2(Factor):
name = "dup_test_factor"
+12 -9
View File
@@ -1,4 +1,5 @@
"""Test built-in factor computation correctness."""
from __future__ import annotations
import numpy as np
@@ -22,15 +23,17 @@ def _make_df(n: int = 120, seed: int = 42) -> pd.DataFrame:
amount = close * vol
dates = pd.date_range("2024-01-01", periods=n, freq="D")
return pd.DataFrame({
"datetime": dates,
"open": open_,
"high": high,
"low": low,
"close": close,
"vol": vol,
"amount": amount,
})
return pd.DataFrame(
{
"datetime": dates,
"open": open_,
"high": high,
"low": low,
"close": close,
"vol": vol,
"amount": amount,
}
)
# ── Auto-registration ──────────────────────────────────────────────
+13 -9
View File
@@ -1,5 +1,6 @@
# tests/unit/test_factor_engine.py
"""Test FactorEngine."""
from __future__ import annotations
import numpy as np
@@ -23,15 +24,17 @@ def _make_df(n: int = 60, seed: int = 42) -> pd.DataFrame:
amount = close * vol
dates = pd.date_range("2024-01-01", periods=n, freq="D")
return pd.DataFrame({
"datetime": dates,
"open": open_,
"high": high,
"low": low,
"close": close,
"vol": vol,
"amount": amount,
})
return pd.DataFrame(
{
"datetime": dates,
"open": open_,
"high": high,
"low": low,
"close": close,
"vol": vol,
"amount": amount,
}
)
class _SimpleMomentum(Factor):
@@ -196,6 +199,7 @@ class TestFactorEngineWithBuiltins:
df = _make_df(200)
from easy_tdx.factor.builtin import list_factors
for f_info in list_factors():
name = f_info["name"]
result = engine.compute_single(df, [name])
+9 -6
View File
@@ -1,5 +1,6 @@
# tests/unit/test_factor_transform.py
"""Test factor preprocessing functions."""
from __future__ import annotations
import numpy as np
@@ -20,12 +21,14 @@ def _make_cross_section(n_dates: int = 20, n_stocks: int = 30, seed: int = 42) -
rows = []
for d in range(n_dates):
for s in range(n_stocks):
rows.append({
"date": 20240101 + d,
"code": f"{s:06d}",
"momentum_20d": rng.normal(0.02, 0.05),
"volatility_20d": abs(rng.normal(0.02, 0.01)),
})
rows.append(
{
"date": 20240101 + d,
"code": f"{s:06d}",
"momentum_20d": rng.normal(0.02, 0.05),
"volatility_20d": abs(rng.normal(0.02, 0.01)),
}
)
df = pd.DataFrame(rows)
df.loc[0, "momentum_20d"] = 10.0
df.loc[1, "momentum_20d"] = -10.0
+14 -9
View File
@@ -1,4 +1,5 @@
"""Test portfolio optimizers."""
from __future__ import annotations
import numpy as np
@@ -15,10 +16,12 @@ from easy_tdx.portfolio.optimizer import (
def _make_scores(n: int = 20, seed: int = 42) -> pd.DataFrame:
rng = np.random.default_rng(seed)
return pd.DataFrame({
"code": [f"{i:06d}" for i in range(n)],
"score": rng.normal(0.02, 0.05, n),
})
return pd.DataFrame(
{
"code": [f"{i:06d}" for i in range(n)],
"score": rng.normal(0.02, 0.05, n),
}
)
class TestEqualWeight:
@@ -57,11 +60,13 @@ class TestRiskParity:
assert abs(sum(w.values()) - 1.0) < 1e-6
def test_with_volatility_column(self):
scores = pd.DataFrame({
"code": ["A", "B", "C"],
"score": [1.0, 1.0, 1.0],
"volatility": [0.1, 0.2, 0.4],
})
scores = pd.DataFrame(
{
"code": ["A", "B", "C"],
"score": [1.0, 1.0, 1.0],
"volatility": [0.1, 0.2, 0.4],
}
)
w = RiskParityOptimizer().optimize(scores, n_stocks=3)
assert w["A"] > w["C"]
+23 -12
View File
@@ -1,4 +1,5 @@
"""Test RebalanceEngine."""
from __future__ import annotations
import numpy as np
@@ -14,13 +15,17 @@ def _make_market(n_stocks: int = 10, n_days: int = 120, seed: int = 42) -> dict[
for i in range(n_stocks):
close = 10.0 + np.cumsum(rng.normal(0.01, 0.5, n_days))
close = np.maximum(close, 1.0)
data[f"{i:06d}"] = pd.DataFrame({
"datetime": pd.date_range("2024-01-01", periods=n_days, freq="D"),
"open": close, "high": close + 0.3, "low": close - 0.3,
"close": close,
"vol": rng.integers(1e5, 1e7, n_days).astype(float),
"amount": close * 1e6,
})
data[f"{i:06d}"] = pd.DataFrame(
{
"datetime": pd.date_range("2024-01-01", periods=n_days, freq="D"),
"open": close,
"high": close + 0.3,
"low": close - 0.3,
"close": close,
"vol": rng.integers(1e5, 1e7, n_days).astype(float),
"amount": close * 1e6,
}
)
return data
@@ -28,8 +33,10 @@ class TestRebalanceEngine:
def test_basic_run(self):
engine = RebalanceEngine(
optimizer=EqualWeightOptimizer(),
factor_name="momentum_20d", n_stocks=5,
rebalance_freq="M", cash=1_000_000,
factor_name="momentum_20d",
n_stocks=5,
rebalance_freq="M",
cash=1_000_000,
)
result = engine.run(_make_market(), start_date=20240101, end_date=20240430)
assert len(result.states) > 0
@@ -40,7 +47,8 @@ class TestRebalanceEngine:
def test_with_factor_weighted(self):
engine = RebalanceEngine(
optimizer=FactorWeightedOptimizer(),
factor_name="momentum_20d", n_stocks=5,
factor_name="momentum_20d",
n_stocks=5,
)
result = engine.run(_make_market(), start_date=20240101, end_date=20240430)
assert len(result.states) > 0
@@ -51,14 +59,17 @@ class TestRebalanceEngine:
def test_equity_curve_dates_sorted(self):
result = RebalanceEngine(
optimizer=EqualWeightOptimizer(), rebalance_freq="M",
optimizer=EqualWeightOptimizer(),
rebalance_freq="M",
).run(_make_market(), start_date=20240101, end_date=20240430)
dates = result.equity_curve["datetime"].tolist()
assert dates == sorted(dates)
def test_trades_recorded(self):
engine = RebalanceEngine(
optimizer=EqualWeightOptimizer(), n_stocks=3, rebalance_freq="M",
optimizer=EqualWeightOptimizer(),
n_stocks=3,
rebalance_freq="M",
)
result = engine.run(_make_market(), start_date=20240101, end_date=20240430)
assert len(result.trades) > 0
+1
View File
@@ -1,4 +1,5 @@
"""Test RiskModel."""
from __future__ import annotations
import numpy as np