diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 47216ef..91b0959 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -78,7 +78,8 @@ "Bash(mypy src/easy_tdx/backtest/types.py tests/unit/test_backtest_types.py)", "Bash(mypy *)", "Bash(git diff *)", - "Bash(echo \"exit: $?\")" + "Bash(echo \"exit: $?\")", + "Bash(where easy-tdx *)" ] } } diff --git a/README.md b/README.md index 3790326..4a203ec 100644 --- a/README.md +++ b/README.md @@ -1081,6 +1081,14 @@ ruff format --check src/ tests/ # format check ## Changelog +### 1.8.1 (2026-06-09) + +**回测增强** — 批量策略对比脚本新增最佳策略完整交易明细输出;版本号统一为单一来源(`pyproject.toml`)。 + +- `run_all_strategies.py` 排名结束后自动输出最佳策略的绩效概要 + 最近 10 笔交易记录 +- 修复 `turtle_breakout` 策略 `TAQ()` 返回 3 值但只解包 2 个的 bug +- 版本号统一:`pyproject.toml` 为唯一来源,`__init__.py` / `cli/__init__.py` / `docs/conf.py` 均动态读取 + ### 1.8.0 (2026-06-09) **回测引擎** — 内置向量回测引擎,支持自定义策略回测和全策略批量对比。 diff --git a/docs/conf.py b/docs/conf.py index eb34a9c..97a3b35 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,8 +3,16 @@ project = "easy-tdx" copyright = "2025, Justin Gu" author = "Justin Gu" + # The full version, including alpha/beta/rc tags -release = "1.8.0" +# Read from package metadata to keep single source of truth (pyproject.toml) +def _get_version() -> str: + from importlib.metadata import version + + return version("easy-tdx") + + +release = _get_version() # -- Extensions --------------------------------------------------------------- extensions = [ diff --git a/pyproject.toml b/pyproject.toml index f874635..3536417 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "easy-tdx" -version = "1.8.0" +version = "1.8.1" description = "通达信 TCP 协议行情数据客户端,支持在线行情、离线数据读取与写入同步" readme = "README.md" requires-python = ">=3.10" diff --git a/run_all_strategies.py b/run_all_strategies.py index 642895e..5b8d08e 100644 --- a/run_all_strategies.py +++ b/run_all_strategies.py @@ -13,6 +13,7 @@ import json import sys import time from pathlib import Path +from typing import Any # 确保 easy_tdx 可导入 sys.path.insert(0, str(Path(__file__).parent / "src")) @@ -75,6 +76,7 @@ def run_all( # 3. 逐个运行策略 results: list[dict] = [] + backtest_results: dict[str, Any] = {} # strategy_name -> BacktestResult for sf in strategy_files: strategy_name = sf.stem @@ -132,6 +134,7 @@ def run_all( "profit_factor": perf.get("profit_factor", 0), "volatility": perf.get("volatility", 0), }) + backtest_results[strategy_name] = result except Exception as e: elapsed = time.perf_counter() - t0 click.echo(f" 错误 ({elapsed:.1f}s): {e}") @@ -220,6 +223,45 @@ def run_all( f"{r['sharpe']:>8.2f} {ret_dd_ratio:>10.2f} {r['win_rate']:>7.1%}" ) + # 最佳策略完整交易明细 + best_name = valid[0]["strategy"] + if best_name in backtest_results: + bt = backtest_results[best_name] + bp = bt.performance + bc = bt.config + + click.echo("\n" + "=" * 80) + click.echo(f"[DETAIL] 最佳策略交易明细: {best_name}") + click.echo("=" * 80) + + click.echo("=== 回测绩效概要 ===") + click.echo(f"总收益率: {bp.get('total_return', 0):.2%}") + click.echo(f"年化收益: {bp.get('annual_return', 0):.2%}") + click.echo(f"最大回撤: {bp.get('max_drawdown', 0):.2%}") + click.echo(f"夏普比率: {bp.get('sharpe', 0):.2f}") + click.echo(f"胜率: {bp.get('win_rate', 0):.2%}") + click.echo(f"交易次数: {bp.get('total_trades', 0)}") + click.echo() + click.echo("=== 配置参数 ===") + click.echo(f"初始资金: {bc.get('cash', 0):.2f}") + click.echo(f"佣金率: {bc.get('commission', 0):.4f}") + click.echo(f"成交规则: {bc.get('execution', 'next_open')}") + click.echo() + + if not bt.trades.empty: + click.echo("=== 最近交易记录 ===") + recent_trades = bt.trades.tail(10) + for _, trade in recent_trades.iterrows(): + direction = "买入" if trade["direction"] == "BUY" else "卖出" + status = "拒绝" if trade["rejected"] else "成交" + click.echo( + f" [{trade['datetime']}] {direction} " + f"数量={trade['size']:.0f} 价格={trade['price']:.2f} " + f"盈亏={trade['pnl']:.2f} [{status}]" + ) + else: + click.echo("无交易记录") + # 报告错误 if errored: click.echo("\n[!] 以下策略运行失败:") diff --git a/src/easy_tdx/__init__.py b/src/easy_tdx/__init__.py index 99b3c78..275fce5 100644 --- a/src/easy_tdx/__init__.py +++ b/src/easy_tdx/__init__.py @@ -107,4 +107,11 @@ __all__ = [ "save_best_ex_host", ] -__version__ = "1.8.0" + +def _get_version() -> str: + from importlib.metadata import version + + return version("easy-tdx") + + +__version__ = _get_version() diff --git a/src/easy_tdx/cli/__init__.py b/src/easy_tdx/cli/__init__.py index a1ce709..1095edb 100644 --- a/src/easy_tdx/cli/__init__.py +++ b/src/easy_tdx/cli/__init__.py @@ -23,7 +23,7 @@ from ..backtest.cli import backtest @click.group() -@click.version_option(version="1.8.0", prog_name="easy-tdx") +@click.version_option(version=__import__("importlib.metadata", fromlist=["version"]).version("easy-tdx"), prog_name="easy-tdx") def cli() -> None: """easy-tdx -- 通达信行情数据 CLI(默认 JSON 输出,适合 Agent 使用)。