import os import sys from datetime import datetime, timedelta import pytest from fastapi.testclient import TestClient PROJECT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) if PROJECT_DIR not in sys.path: sys.path.insert(0, PROJECT_DIR) import altcoin_db import web_server @pytest.fixture def temp_db(monkeypatch, tmp_path): db_path = tmp_path / "altcoin_monitor.db" monkeypatch.setattr(altcoin_db, "DB_PATH", str(db_path)) monkeypatch.setattr(web_server, "init_db", altcoin_db.init_db) monkeypatch.setattr(web_server, "get_review_stats", altcoin_db.get_review_stats) monkeypatch.setattr(web_server, "get_stats", altcoin_db.get_stats) altcoin_db.init_db() return db_path def test_iteration_log_supports_rule_diff_and_effect_summary(temp_db): conn = altcoin_db.get_conn() base_time = datetime(2026, 4, 29, 10, 0, 0) for idx, (outcome, pnl) in enumerate([ ("爆发", 12.5), ("失败", -4.2), ("横盘", 0.8), ], start=1): conn.execute( """ INSERT INTO review_log (rec_id, symbol, review_time, outcome, pnl_48h, max_pnl_48h, triggered_signals, hit_signals, miss_signals, lesson) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( idx, f"COIN{idx}/USDT", (base_time + timedelta(hours=idx)).isoformat(), outcome, pnl, 10 + idx, '[]', '[]', '[]', f"lesson-{idx}" ) ) conn.commit() conn.close() altcoin_db.log_strategy_iteration( run_date="2026-04-29", trigger_source="daily_review", title="增加静K蓄力优先级", summary="基于漏选币复盘,提高静K蓄力相关因子的优先级。", findings=["静K蓄力类漏选较集中"], problems=["旧参数对蓄力型起爆敏感度不足"], actions=["提高静K蓄力权重并降低滞后信号优先级"], changed_rules=[{"field": "signal_weights.静K蓄力", "old": 1.0, "new": 1.6}], metrics={"reviews_done": 3}, related_symbols=["PNT/USDT"], config_diff={ "changed": [ {"path": "signal_weights.静K蓄力", "old": 1.0, "new": 1.6}, {"path": "meta.iteration_count", "old": 4, "new": 5}, ], "added": [ {"path": "learned_rules.rule_20260429_001", "new": {"description": "静K蓄力增强"}}, ], "removed": [] }, effect_summary={ "review_count_window": 3, "hit_rate_pct": 33.3, "avg_pnl": 3.03, "fail_rate_pct": 33.3, "flat_rate_pct": 33.3, } ) logs = altcoin_db.get_strategy_iteration_logs(limit=5) assert logs[0]["config_diff"]["changed"][0]["path"] == "signal_weights.静K蓄力" assert logs[0]["effect_summary"]["avg_pnl"] == 3.03 summary = altcoin_db.get_strategy_iteration_summary(days=30) assert summary["config_change_count"] == 3 assert summary["effect_overview"]["avg_hit_rate_pct"] == 33.3 def test_review_api_returns_diff_and_effect_fields(temp_db): altcoin_db.log_strategy_iteration( run_date="2026-04-29", trigger_source="manual", title="补充diff与效果回看", summary="让网站能看见参数改了多少,以及改后效果。", findings=["用户需要参数审计"], problems=["之前只能看文字描述"], actions=["给迭代日志增加config_diff/effect_summary"], changed_rules=[], metrics={"items": 1}, related_symbols=[], config_diff={"changed": [{"path": "review.min_samples", "old": 5, "new": 4}], "added": [], "removed": []}, effect_summary={"review_count_window": 0, "hit_rate_pct": 0, "avg_pnl": 0}, ) client = TestClient(web_server.app) resp = client.get("/api/review") assert resp.status_code == 200 data = resp.json() assert data["iteration_logs"][0]["config_diff"]["changed"][0]["path"] == "review.min_samples" assert "effect_overview" in data["iteration_summary"]