From b4f63c85a69a5c871dbf5975253abb8638a4f0cb Mon Sep 17 00:00:00 2001 From: GitHub Date: Wed, 10 Jun 2026 18:53:06 +0800 Subject: [PATCH] feat: add OBV trend strategy (v1.9.5) - New obv_trend.py strategy: OBV energy flow trend following - Entry: OBV > MAOBV*1.02 buffer + MAOBV 20-bar uptrend confirmed - Exit: OBV drops below MAOBV - Tunable params: maobv_period(30), maobv_lookback(20), obv_buffer(0.02) - Update README changelog and strategy table - Bump version to 1.9.5 Co-Authored-By: Claude Opus 4.8 --- README.md | 13 ++++++++++- pyproject.toml | 2 +- strategies/README.md | 1 + strategies/obv_trend.py | 48 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 strategies/obv_trend.py diff --git a/README.md b/README.md index 9f38e2f..f309d48 100644 --- a/README.md +++ b/README.md @@ -438,7 +438,7 @@ python -X utf8 run_all_strategies.py SH 600519 --count 2000 --cash 1000000 --adj #### 自带策略示例 -`strategies/` 目录下有 15 个开箱即用的策略文件,可直接用于 `--strategy-file`: +`strategies/` 目录下有 16 个开箱即用的策略文件,可直接用于 `--strategy-file`: | 文件 | 策略 | 类型 | 适合行情 | |------|------|------|----------| @@ -457,6 +457,7 @@ python -X utf8 run_all_strategies.py SH 600519 --count 2000 --cash 1000000 --adj | `mfi_volume.py` | MFI 量价反转 | 量价反转 | 震荡市(带量能确认) | | `trix_cross.py` | TRIX 三重平滑趋势交叉 | 趋势跟踪 | 中长线(抗噪音) | | `mtm_momentum.py` | MTM 动量零线穿越 | 动量 | 趋势拐点 | +| `obv_trend.py` | OBV 能量潮趋势 | 量价趋势 | 资金持续流入的上升趋势 | 编写自定义策略只需继承 `Strategy` 基类: @@ -1251,6 +1252,16 @@ ruff format --check src/ tests/ # format check ## Changelog +### 1.9.5 (2026-06-10) + +**OBV 能量潮趋势策略** — 新增 `obv_trend.py` 策略,基于 OBV 与其 30 日均线 MAOBV 的关系判断多空方向。 + +- 新增 `strategies/obv_trend.py`:OBV 能量潮趋势策略 +- 入场条件:OBV 超过 MAOBV 达 2% 缓冲带 且 MAOBV 趋势向上(20 根确认) +- 出场条件:OBV 跌破 MAOBV,资金流向转空即离场 +- MAOBV 趋势仅作入场过滤(确认趋势存在),出场只看 OBV/MAOBV 交叉信号 +- 可调参数:`maobv_period`(30)、`maobv_lookback`(20)、`obv_buffer`(0.02) + ### 1.9.4 (2026-06-10) **Bug 修复** — 修复 `easy-tdx version` 命令硬编码版本号的问题,改为从 `pyproject.toml` 动态读取。 diff --git a/pyproject.toml b/pyproject.toml index 16b9ac7..60d4c53 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "easy-tdx" -version = "1.9.4" +version = "1.9.5" description = "通达信 TCP 协议行情数据客户端,支持在线行情、离线数据读取与写入同步" readme = "README.md" requires-python = ">=3.10" diff --git a/strategies/README.md b/strategies/README.md index 517caf3..383914f 100644 --- a/strategies/README.md +++ b/strategies/README.md @@ -23,6 +23,7 @@ easy-tdx backtest SH 600519 --strategy-file strategies/macd_cross.py --cash 5000 | `turtle_breakout.py` | 海龟交易法(唐安奇通道) | 趋势突破 | 牛市启动 | | `bias_reversal.py` | 乖离率反转 | 反转 | 震荡回归 | | `volume_price.py` | 量价配合 | 综合判断 | 放量突破 | +| `obv_trend.py` | OBV 能量潮趋势 | 量价趋势 | 资金持续流入的上升趋势 | ## 编写自定义策略 diff --git a/strategies/obv_trend.py b/strategies/obv_trend.py new file mode 100644 index 0000000..25c5d94 --- /dev/null +++ b/strategies/obv_trend.py @@ -0,0 +1,48 @@ +"""OBV 能量潮趋势策略。 + +OBV 超过 MAOBV(30 日均线)一定缓冲带后确认多头,MAOBV 自身趋势向上时买入。 +OBV 跌破 MAOBV 时卖出——资金流向转空即离场。 + +OBV 是累计成交量指标:价涨加量、价跌减量,反映资金进出方向。 +MAOBV 平滑后的趋势比原始 OBV 更稳定,适合过滤噪声。 +入场要求 OBV 领先 MAOBV 达到缓冲带幅度,过滤交叉区域的假突破噪声。 + +用法:: + + easy-tdx backtest SZ 000001 --strategy-file strategies/obv_trend.py --count 500 --table +""" + +from easy_tdx import MyTT +from easy_tdx.backtest import Strategy + + +class OBVTrendStrategy(Strategy): + """OBV 能量潮趋势策略。""" + + maobv_period: int = 30 # MAOBV 均线周期 + maobv_lookback: int = 20 # MAOBV 趋势判定回溯根数 + obv_buffer: float = 0.02 # OBV 入场缓冲带(2%) + + def init(self) -> None: + self.obv = self.I(MyTT.OBV, self.data.close, self.data.vol) + self.maobv = self.I(MyTT.MA, self.obv, self.maobv_period) + + def next(self) -> None: + idx = self._bar_index + obv = float(self.obv[idx]) + maobv = float(self.maobv[idx]) + + # MAOBV 趋势向上:当前值 > N 根前的值 + lookback_idx = max(0, idx - self.maobv_lookback) + maobv_prev = float(self.maobv[lookback_idx]) + maobv_rising = maobv > maobv_prev + + # 入场:OBV 超过 MAOBV 达缓冲带幅度 且 MAOBV 趋势向上 + if obv > maobv * (1 + self.obv_buffer) and maobv_rising: + if self.position["size"] == 0: + self.buy(size=0) + + # 出场:OBV < MAOBV(空头信号) + elif obv < maobv: + if self.position["size"] > 0: + self.sell(size=0)