fix(rotation): 回测导出JSON序列化NaN/Inf清洗

- simple_rotation.py: 新增 _sanitize_json() 递归替换 NaN/Inf 为 None,
  确保 json.dump 生成合法 JSON(避免前端解析失败)
- .env: 注释掉群2钉钉配置(暂不使用)
This commit is contained in:
2026-06-03 09:14:53 +08:00
parent 4f9e0231bd
commit d700bc1dfd
2 changed files with 20 additions and 2 deletions

View File

@@ -40,6 +40,22 @@ def _http_get(url: str, params: dict = None, timeout: int = 120) -> requests.Res
return _session.get(url, params=params, timeout=timeout)
def _sanitize_json(obj):
"""Recursively replace NaN/Inf with None in-place so json.dump produces valid JSON"""
if isinstance(obj, dict):
for k, v in obj.items():
if isinstance(v, float) and (math.isnan(v) or math.isinf(v)):
obj[k] = None
elif isinstance(v, (dict, list)):
_sanitize_json(v)
elif isinstance(obj, list):
for i, v in enumerate(obj):
if isinstance(v, float) and (math.isnan(v) or math.isinf(v)):
obj[i] = None
elif isinstance(v, (dict, list)):
_sanitize_json(v)
# ============================================================
# Pure functions: momentum
# ============================================================
@@ -888,6 +904,7 @@ class SimpleRotationStrategy:
},
'days': days_out,
}
_sanitize_json(detail)
with open(detail_path, 'w', encoding='utf-8') as f:
json.dump(detail, f, ensure_ascii=False, indent=2)
print(f" + Detail: {detail_path} ({len(days_out)} days)")
@@ -895,6 +912,7 @@ class SimpleRotationStrategy:
# Metrics JSON
metrics = self._compute_metrics(sum(1 for r in self.daily_records if r['is_rebalance']))
metrics_path = output_dir / 'simple_rotation_metrics.json'
_sanitize_json(metrics)
with open(metrics_path, 'w', encoding='utf-8') as f:
json.dump(metrics, f, ensure_ascii=False, indent=2)
print(f" + Metrics: {metrics_path}")