diff --git a/src/easy_tdx/MyTT.py b/src/easy_tdx/MyTT.py index dc336e4..f33a9dc 100644 --- a/src/easy_tdx/MyTT.py +++ b/src/easy_tdx/MyTT.py @@ -15,6 +15,7 @@ # V3.3 2023-11-09 新增 SIN,COS,TAN序列处理的三角函数 # V4.0 2026-06-02 handsomejustin 新增 ZHUOYAO,BIAS_SIGNAL两个自创函数 # V4.1 2026-06-14 新增 SAR(抛物线转向), VWAP(成交量加权均价), AROON(阿隆指标); 注册 FK +# V4.2 2026-07-09 新增 FSL(分水岭指标) # 以下所有函数如无特别说明,输入参数S均为numpy序列或者列表list,N为整型int # 应用层1级函数完美兼容通达信或同花顺,具体使用方法请参考通达信 @@ -559,4 +560,15 @@ def AROON(HIGH, LOW, N=25): # 阿隆指标:趋势启动时机识别(N周期 return RD(AROON_UP), RD(AROON_DOWN), RD(OSC) +def FSL(CLOSE, VOL, CAPITAL): # 分水岭指标:多空趋势强弱分界(SWS含换手率动态平滑) + # SWL = (EMA(C,5)*7 + EMA(C,10)*3) / 10 : 5日/10日指数均值的加权合成 + SWL = (EMA(CLOSE, 5) * 7 + EMA(CLOSE, 10) * 3) / 10 + # SWS = DMA(EMA(C,12), MAX(1, 100*SUM(VOL,5)/(3*CAPITAL))) + # 平滑因子 = 5日成交量换手率放大值,CAPITAL 为流通股本 + A = MAX(1, 100 * (SUM(VOL, 5) / (3 * CAPITAL))) + A = MIN(A, 1.0) # 模拟通达信 DMA(X,A) 内部钳制 A<=1,避免序列因子越界发散 + SWS = DMA(EMA(CLOSE, 12), A) + return RD(SWL), RD(SWS) + + # 望大家能提交更多指标和函数 https://github.com/mpquant/MyTT diff --git a/src/easy_tdx/MyTT.pyi b/src/easy_tdx/MyTT.pyi index fa2d79f..ddb6538 100644 --- a/src/easy_tdx/MyTT.pyi +++ b/src/easy_tdx/MyTT.pyi @@ -105,6 +105,11 @@ def AROON( N: int = ..., ) -> tuple[NDArray, NDArray, NDArray]: ... def FK(CLOSE: npt.ArrayLike) -> NDArray: ... +def FSL( + CLOSE: npt.ArrayLike, + VOL: npt.ArrayLike, + CAPITAL: float, +) -> tuple[NDArray, NDArray]: ... # ── Utility Functions ──────────────────────────────────────────────────────── diff --git a/src/easy_tdx/backtest/strategies/builtin.py b/src/easy_tdx/backtest/strategies/builtin.py index 6ab0939..61e2642 100644 --- a/src/easy_tdx/backtest/strategies/builtin.py +++ b/src/easy_tdx/backtest/strategies/builtin.py @@ -25,6 +25,7 @@ from easy_tdx.MyTT import ( DPO, EMA, EMV, + FSL, KDJ, KTN, MA, @@ -552,3 +553,38 @@ class AtrBreakoutStrategy(ParametrizedStrategy): self.buy() elif close <= lower and self.position["size"] > 0: self.sell() + + +# ── FSL 分水岭指标 ──────────────────────────────────────────────────────────── + + +@register_strategy( + name="fsl", + label="FSL 分水岭", + description="SWL 上穿 SWS 买入(多头占优),SWL 下穿 SWS 卖出(空头占优)。", +) +class FslStrategy(ParametrizedStrategy): + """FSL 分水岭 SWL/SWS 金叉死叉。""" + + params = [ + Param( + "capital", + float, + default=1e8, + min_value=1e6, + max_value=1e12, + label="流通股本(股)", + ), + ] + + def init(self) -> None: + self.swl, self.sws = self.I(FSL, self.data.close, self.data.vol, self.p["capital"]) + self.gold = self.I(CROSS, self.swl, self.sws) + self.dead = self.I(CROSS, self.sws, self.swl) + + def next(self) -> None: + i = self._bar_index + if self.gold[i]: + self.buy() + elif self.dead[i] and self.position["size"] > 0: + self.sell() diff --git a/src/easy_tdx/backtest/strategies/presets.py b/src/easy_tdx/backtest/strategies/presets.py index e70eaad..2f360f5 100644 --- a/src/easy_tdx/backtest/strategies/presets.py +++ b/src/easy_tdx/backtest/strategies/presets.py @@ -93,6 +93,11 @@ STRATEGY_PRESETS: dict[str, dict[str, list[Any]]] = { "dpo": { "m1": [15, 20, 30], }, # 3 + # ── 分水岭类 ───────────────────────────────────────────────────────────── + "fsl": { + # capital 仅作粗档扫描(1千万/1亿/10亿股),覆盖小盘→大盘 + "capital": [1e7, 1e8, 1e9, 1e10], + }, # 4 } diff --git a/tests/unit/test_web_backtest.py b/tests/unit/test_web_backtest.py index 81ec2f5..2f8f75c 100644 --- a/tests/unit/test_web_backtest.py +++ b/tests/unit/test_web_backtest.py @@ -59,7 +59,8 @@ def test_registry_has_builtin_strategies(): assert "boll_breakout" in names assert "rsi_reversal" in names assert "kdj_cross" in names - assert len(names) >= 18 + assert "fsl" in names + assert len(names) >= 19 def test_strategy_schema_serialization():