From 9c972c2673a2c3e3fcbcec65de996d0c9794e873 Mon Sep 17 00:00:00 2001 From: YuWuKunCheng Date: Fri, 19 Jun 2026 20:59:25 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A7=BB=E9=99=A4=20=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=85=BC=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chan.py | 325 ++++++++++----------------- chanlun-py/Cargo.toml | 2 +- chanlun-py/chanlun.pyi | 1 + chanlun-py/chanlun/__init__.pyi | 1 + chanlun-py/chanlun/chan.py | 325 ++++++++++----------------- chanlun-py/pyproject.toml | 2 +- chanlun-py/src/config_py.rs | 5 + chanlun/src/business/multi_frame.rs | 7 +- chanlun/src/config.rs | 197 +++------------- chanlun/src/indicators/calculator.rs | 302 ++++++++++--------------- 10 files changed, 377 insertions(+), 790 deletions(-) diff --git a/chan.py b/chan.py index 24f2d17..e64128d 100644 --- a/chan.py +++ b/chan.py @@ -846,7 +846,7 @@ class 缠论配置: **[线段]** 线段_特征序列忽视老阴老阳, 线段_缺口后紧急修正, 线段内部中枢图显 等 \\ **[分析开关]** 分析笔, 分析线段, 分析扩展线段, 分析笔中枢, 分析线段中枢 \\ **[指标]** 计算指标, 指标计算方式, MACD/RSI/KDJ 参数 \\ - **[推送/显示]** 图表展示, 推送K线/笔/线段/中枢 等 \\ + **[推送/显示]** 图表展示 (主开关), 图表展示标签 (标签列表) 等 \\ **[买卖点]** 买卖点偏移, 买卖点激进识别, 买卖点_背离率, 买卖点_计算方式 等 \\ **[背驰]** 线段内部背驰_MACD, 线段内部背驰_斜率 等 \\ **[其他]** 手动终止, 加载文件路径 @@ -885,45 +885,15 @@ class 缠论配置: 分析线段中枢: bool = True, 手动终止: str = "", # 2099-12-31 00:00:00 计算指标: bool = True, - 指标计算方式: str = "收", # (开, 高, 低, 收, 高低均值, 高低收均值, 开高低收均值), 默认 收盘价 - 平滑异同移动平均线_快线周期: int = 13, - 平滑异同移动平均线_慢线周期: int = 31, - 平滑异同移动平均线_信号周期: int = 11, - 相对强弱指数_周期: int = 13, - 相对强弱指数_移动平均线周期: int = 13, - 相对强弱指数_超买阈值: float = 75.0, - 相对强弱指数_超卖阈值: float = 25.0, - 随机指标_RSV周期: int = 13, - 随机指标_K值平滑周期: int = 5, - 随机指标_D值平滑周期: int = 5, - 随机指标_超买阈值: float = 80.0, - 随机指标_超卖阈值: float = 20.0, - 计算BOLL: bool = False, - 布林带_周期: int = 20, - 布林带_标准差倍数: float = 2.0, - 均线_类型列表: List[str] = None, - 均线_周期列表: List[int] = None, + 指标计算方式: str = "收", # 均线计算方式 + 均线参数列表: List[tuple] = None, # [(key, 计算方式, 类型, 周期), ...] # 多参数指标列表(None/空列表 = 使用默认单参数) - MACD_参数列表: List[tuple] = None, # [(key, 快线, 慢线, 信号), ...] - RSI_周期列表: List[tuple] = None, # [(key, 周期), ...] - KDJ_参数列表: List[tuple] = None, # [(key, RSV周期, K平滑, D平滑), ...] - BOLL_参数列表: List[tuple] = None, # [(key, 周期, 标准差倍数), ...] - 图表展示: bool = True, - 推送K线: bool = True, - 推送笔: bool = True, - 推送线段: bool = True, - 推送中枢: bool = True, - 图表展示_笔: bool = True, - 图表展示_线段: bool = True, - 图表展示_扩展线段: bool = True, - 图表展示_扩展线段_线段: bool = True, - 图表展示_线段_线段: bool = True, - 图表展示_中枢_笔: bool = True, - 图表展示_中枢_线段: bool = True, - 图表展示_中枢_扩展线段: bool = True, - 图表展示_中枢_扩展线段_线段: bool = True, - 图表展示_中枢_线段_线段: bool = True, - 图表展示_中枢_线段内部: bool = True, + MACD_参数列表: List[tuple] = None, # [(key, 计算方式, 快线, 慢线, 信号), ...] + RSI_周期列表: List[tuple] = None, # [(key, 计算方式, 周期), ...] + KDJ_参数列表: List[tuple] = None, # [(key, 计算方式, RSV周期, K平滑, D平滑), ...] + BOLL_参数列表: List[tuple] = None, # [(key, 计算方式, 周期, 标准差倍数), ...] + 图表展示: bool = True, # 图表系统主开关 + 图表展示标签: Optional[List[str]] = None, # None=全部展示, []=不展示, [\"笔\",\"线段\"]=指定 买卖点偏移: int = 1, # 最大偏移 买卖点激进识别: bool = False, # 激进模式下将不考虑分型的完整性 买卖点与MACD柱强相关: bool = False, # True: 卖点需正值 买点需负值 @@ -963,43 +933,13 @@ class 缠论配置: self.手动终止 = 手动终止 self.计算指标 = 计算指标 self.指标计算方式 = 指标计算方式 - self.平滑异同移动平均线_快线周期 = 平滑异同移动平均线_快线周期 - self.平滑异同移动平均线_慢线周期 = 平滑异同移动平均线_慢线周期 - self.平滑异同移动平均线_信号周期 = 平滑异同移动平均线_信号周期 - self.相对强弱指数_周期 = 相对强弱指数_周期 - self.相对强弱指数_移动平均线周期 = 相对强弱指数_移动平均线周期 - self.相对强弱指数_超买阈值 = 相对强弱指数_超买阈值 - self.相对强弱指数_超卖阈值 = 相对强弱指数_超卖阈值 - self.随机指标_RSV周期 = 随机指标_RSV周期 - self.随机指标_K值平滑周期 = 随机指标_K值平滑周期 - self.随机指标_D值平滑周期 = 随机指标_D值平滑周期 - self.随机指标_超买阈值 = 随机指标_超买阈值 - self.随机指标_超卖阈值 = 随机指标_超卖阈值 - self.计算BOLL = 计算BOLL - self.布林带_周期 = 布林带_周期 - self.布林带_标准差倍数 = 布林带_标准差倍数 - self.均线_类型列表 = 均线_类型列表 if 均线_类型列表 is not None else [] - self.均线_周期列表 = 均线_周期列表 if 均线_周期列表 is not None else [] - self.MACD_参数列表 = MACD_参数列表 if MACD_参数列表 is not None else [] - self.RSI_周期列表 = RSI_周期列表 if RSI_周期列表 is not None else [] - self.KDJ_参数列表 = KDJ_参数列表 if KDJ_参数列表 is not None else [] - self.BOLL_参数列表 = BOLL_参数列表 if BOLL_参数列表 is not None else [] + self.均线参数列表 = 均线参数列表 if 均线参数列表 is not None else [] + self.MACD_参数列表 = MACD_参数列表 if MACD_参数列表 is not None else [("macd", "收", 13, 31, 11)] + self.RSI_周期列表 = RSI_周期列表 if RSI_周期列表 is not None else [("rsi", "收", 14, 13, 75.0, 25.0)] + self.KDJ_参数列表 = KDJ_参数列表 if KDJ_参数列表 is not None else [("kdj", "收", 13, 5, 5, 80.0, 20.0)] + self.BOLL_参数列表 = BOLL_参数列表 if BOLL_参数列表 is not None else [("boll", "收", 20, 2.0)] self.图表展示 = 图表展示 - self.推送K线 = 推送K线 - self.推送笔 = 推送笔 - self.推送线段 = 推送线段 - self.推送中枢 = 推送中枢 - self.图表展示_笔 = 图表展示_笔 - self.图表展示_线段 = 图表展示_线段 - self.图表展示_扩展线段 = 图表展示_扩展线段 - self.图表展示_扩展线段_线段 = 图表展示_扩展线段_线段 - self.图表展示_线段_线段 = 图表展示_线段_线段 - self.图表展示_中枢_笔 = 图表展示_中枢_笔 - self.图表展示_中枢_线段 = 图表展示_中枢_线段 - self.图表展示_中枢_扩展线段 = 图表展示_中枢_扩展线段 - self.图表展示_中枢_扩展线段_线段 = 图表展示_中枢_扩展线段_线段 - self.图表展示_中枢_线段_线段 = 图表展示_中枢_线段_线段 - self.图表展示_中枢_线段内部 = 图表展示_中枢_线段内部 + self.图表展示标签 = set(图表展示标签) if 图表展示标签 is not None else None self.买卖点偏移 = 买卖点偏移 self.买卖点激进识别 = 买卖点激进识别 self.买卖点与MACD柱强相关 = 买卖点与MACD柱强相关 @@ -1049,43 +989,13 @@ class 缠论配置: "手动终止": {"annotation": str, "default": ""}, "计算指标": {"annotation": bool, "default": True}, "指标计算方式": {"annotation": str, "default": "收"}, - "平滑异同移动平均线_快线周期": {"annotation": int, "default": 13}, - "平滑异同移动平均线_慢线周期": {"annotation": int, "default": 31}, - "平滑异同移动平均线_信号周期": {"annotation": int, "default": 11}, - "相对强弱指数_周期": {"annotation": int, "default": 13}, - "相对强弱指数_移动平均线周期": {"annotation": int, "default": 13}, - "相对强弱指数_超买阈值": {"annotation": float, "default": 75.0}, - "相对强弱指数_超卖阈值": {"annotation": float, "default": 25.0}, - "随机指标_RSV周期": {"annotation": int, "default": 13}, - "随机指标_K值平滑周期": {"annotation": int, "default": 5}, - "随机指标_D值平滑周期": {"annotation": int, "default": 5}, - "随机指标_超买阈值": {"annotation": float, "default": 80.0}, - "随机指标_超卖阈值": {"annotation": float, "default": 20.0}, - "计算BOLL": {"annotation": bool, "default": False}, - "布林带_周期": {"annotation": int, "default": 20}, - "布林带_标准差倍数": {"annotation": float, "default": 2.0}, - "均线_类型列表": {"annotation": List[str], "default": []}, - "均线_周期列表": {"annotation": List[int], "default": []}, - "MACD_参数列表": {"annotation": List[tuple], "default": []}, - "RSI_周期列表": {"annotation": List[tuple], "default": []}, - "KDJ_参数列表": {"annotation": List[tuple], "default": []}, - "BOLL_参数列表": {"annotation": List[tuple], "default": []}, + "均线参数列表": {"annotation": List[tuple], "default": []}, + "MACD_参数列表": {"annotation": List[tuple], "default": [("macd", "收", 13, 31, 11)]}, + "RSI_周期列表": {"annotation": List[tuple], "default": [("rsi", "收", 14, 13, 75.0, 25.0)]}, + "KDJ_参数列表": {"annotation": List[tuple], "default": [("kdj", "收", 13, 5, 5, 80.0, 20.0)]}, + "BOLL_参数列表": {"annotation": List[tuple], "default": [("boll", "收", 20, 2.0)]}, "图表展示": {"annotation": bool, "default": True}, - "推送K线": {"annotation": bool, "default": True}, - "推送笔": {"annotation": bool, "default": True}, - "推送线段": {"annotation": bool, "default": True}, - "推送中枢": {"annotation": bool, "default": True}, - "图表展示_笔": {"annotation": bool, "default": True}, - "图表展示_线段": {"annotation": bool, "default": True}, - "图表展示_扩展线段": {"annotation": bool, "default": True}, - "图表展示_扩展线段_线段": {"annotation": bool, "default": True}, - "图表展示_线段_线段": {"annotation": bool, "default": True}, - "图表展示_中枢_笔": {"annotation": bool, "default": True}, - "图表展示_中枢_线段": {"annotation": bool, "default": True}, - "图表展示_中枢_扩展线段": {"annotation": bool, "default": True}, - "图表展示_中枢_扩展线段_线段": {"annotation": bool, "default": True}, - "图表展示_中枢_线段_线段": {"annotation": bool, "default": True}, - "图表展示_中枢_线段内部": {"annotation": bool, "default": True}, + "图表展示标签": {"annotation": Optional[List[str]], "default": None}, "买卖点偏移": {"annotation": int, "default": 1}, "买卖点激进识别": {"annotation": bool, "default": False}, "买卖点与MACD柱强相关": {"annotation": bool, "default": False}, @@ -1137,27 +1047,45 @@ class 缠论配置: logger.warning(f"[{fname}] = {value} 解析失败,使用默认值:{default}") setattr(self, fname, default) - # ---- 参数列表解析:多参数列表为空时回退到默认单参数 ---- + def 设置指标( + self, + *, + 均线: List[tuple] = None, + MACD: List[tuple] = None, + RSI: List[tuple] = None, + KDJ: List[tuple] = None, + BOLL: List[tuple] = None, + ): + """统一设置所有指标参数。 - def _解析MACD参数列表(self) -> List[tuple]: - if self.MACD_参数列表: - return self.MACD_参数列表 - return [("macd", self.平滑异同移动平均线_快线周期, self.平滑异同移动平均线_慢线周期, self.平滑异同移动平均线_信号周期)] + 元组格式 ``(key, 计算方式, *params)``: + - 均线: ``("SMA_5", "收", "SMA", 5)`` — key/计算方式/类型/周期 + - MACD: ``("默认", "收", 13, 31, 11)`` — 快线/慢线/信号 + - RSI: ``("默认", "收", 14, 13, 75, 25)`` — 周期/MA周期/超买/超卖 + - KDJ: ``("默认", "收", 13, 5, 5, 80, 20)`` — RSV/K平滑/D平滑/超买/超卖 + - BOLL: ``("默认", "收", 20, 2.0)`` — 周期/标准差倍数 - def _解析RSI周期列表(self) -> List[tuple]: - if self.RSI_周期列表: - return self.RSI_周期列表 - return [("rsi", self.相对强弱指数_周期)] + :param 均线: key 即均线名(如 ``"SMA_5"``),同时编码类型和周期 + :param MACD: 首个 key 同时写入 ``"macd"`` 兼容槽位 + :param BOLL: BOLL 参数元组列表,为空则不计算 + """ + self.计算指标 = True + if 均线 is not None: + self.均线参数列表 = 均线 + if MACD is not None: + self.MACD_参数列表 = MACD + if RSI is not None: + self.RSI_周期列表 = RSI + if KDJ is not None: + self.KDJ_参数列表 = KDJ + if BOLL is not None: + self.BOLL_参数列表 = BOLL - def _解析KDJ参数列表(self) -> List[tuple]: - if self.KDJ_参数列表: - return self.KDJ_参数列表 - return [("kdj", self.随机指标_RSV周期, self.随机指标_K值平滑周期, self.随机指标_D值平滑周期)] - - def _解析BOLL参数列表(self) -> List[tuple]: - if self.BOLL_参数列表: - return self.BOLL_参数列表 - return [("boll", self.布林带_周期, self.布林带_标准差倍数)] + def 展示标签(self, 标签: str) -> bool: + """判断指定标签是否应展示。None = 全部展示,空列表 = 全部隐藏。""" + if self.图表展示标签 is None: + return True + return 标签 in self.图表展示标签 @classmethod def 兼容旧版本配置(cls, values: Dict[str, Any]) -> Dict[str, Any]: @@ -1190,7 +1118,13 @@ class 缠论配置: :return: 包含所有配置字段的字典 """ - return {k: getattr(self, k) for k in self.model_fields().keys()} + result = {} + for k in self.model_fields().keys(): + v = getattr(self, k) + if isinstance(v, set): + v = list(v) + result[k] = v + return result def to_json(self) -> str: """将配置序列化为 JSON 字符串。 @@ -1246,21 +1180,7 @@ class 缠论配置: return cls( 线段内部中枢图显=False, 图表展示=False, - 推送K线=False, - 推送笔=False, - 推送线段=False, - 推送中枢=False, - 图表展示_笔=False, - 图表展示_线段=False, - 图表展示_扩展线段=False, - 图表展示_扩展线段_线段=False, - 图表展示_线段_线段=False, - 图表展示_中枢_笔=False, - 图表展示_中枢_线段=False, - 图表展示_中枢_扩展线段=False, - 图表展示_中枢_扩展线段_线段=False, - 图表展示_中枢_线段_线段=False, - 图表展示_中枢_线段内部=False, + 图表展示标签=[], ) def model_copy(self, update: dict = None, deep: bool = True): @@ -2479,19 +2399,21 @@ class 指标计算器: @staticmethod def _计算MACD组(当前K线: K线, prev: Optional[指标容器], 配置: 缠论配置): idx = 当前K线.指标 - 计算方式 = 配置.指标计算方式 - for key, 快, 慢, 信号 in 配置._解析MACD参数列表(): + for i, (key, 计算方式, *params) in enumerate(配置.MACD_参数列表): + 快, 慢, 信号 = params[0], params[1], params[2] prev_val = prev[key] if prev is not None and key in prev else None if prev_val is not None: idx[key] = 平滑异同移动平均线.增量计算_K线(prev_val, 当前K线, 计算方式) else: idx[key] = 平滑异同移动平均线.首次计算_K线(当前K线, 计算方式, 快, 慢, 信号) + if i == 0: + idx["macd"] = idx[key] @staticmethod def _计算RSI组(当前K线: K线, prev: Optional[指标容器], 配置: 缠论配置): idx = 当前K线.指标 - 计算方式 = 配置.指标计算方式 - for key, 周期 in 配置._解析RSI周期列表(): + for key, 计算方式, *params in 配置.RSI_周期列表: + 周期, MA周期, 超买, 超卖 = params[0], params[1], params[2], params[3] prev_val = prev[key] if prev is not None and key in prev else None if prev_val is not None: idx[key] = 相对强弱指数.增量计算_K线(prev_val, 当前K线, 计算方式) @@ -2500,16 +2422,16 @@ class 指标计算器: 当前K线, 计算方式, 周期, - 配置.相对强弱指数_超买阈值, - 配置.相对强弱指数_超卖阈值, - 配置.相对强弱指数_移动平均线周期, + 超买, + 超卖, + MA周期, ) @staticmethod def _计算KDJ组(当前K线: K线, prev: Optional[指标容器], 配置: 缠论配置): idx = 当前K线.指标 - 计算方式 = 配置.指标计算方式 - for key, rsv, k平滑, d平滑 in 配置._解析KDJ参数列表(): + for key, 计算方式, *params in 配置.KDJ_参数列表: + rsv, k平滑, d平滑, 超买, 超卖 = params[0], params[1], params[2], params[3], params[4] prev_val = prev[key] if prev is not None and key in prev else None if prev_val is not None: idx[key] = 随机指标.增量计算_K线(prev_val, 当前K线, 计算方式) @@ -2520,15 +2442,15 @@ class 指标计算器: rsv, k平滑, d平滑, - 配置.随机指标_超买阈值, - 配置.随机指标_超卖阈值, + 超买, + 超卖, ) @staticmethod def _计算BOLL组(当前K线: K线, prev: Optional[指标容器], 配置: 缠论配置): idx = 当前K线.指标 - 计算方式 = 配置.指标计算方式 - for key, 周期, 标准差倍数 in 配置._解析BOLL参数列表(): + for key, 计算方式, *params in 配置.BOLL_参数列表: + 周期, 标准差倍数 = params[0], params[1] prev_val = prev[key] if prev is not None and key in prev else None if prev_val is not None: idx[key] = 布林带.增量计算(prev_val, 当前K线, 计算方式) @@ -2537,18 +2459,16 @@ class 指标计算器: @staticmethod def _更新均线(当前K线: K线, 普K序列: List[K线], 配置: 缠论配置): - if not 配置.均线_类型列表 or not 配置.均线_周期列表: + if not 配置.均线参数列表: return - for ma_type in 配置.均线_类型列表: - for period in 配置.均线_周期列表: - key = f"{ma_type}_{period}" - if ma_type == "SMA": - 当前K线.指标.均线[key] = 均线工具.增量SMA(普K序列, period, 配置.指标计算方式) - elif ma_type == "EMA": - 前值 = None - if len(普K序列) >= 2: - 前值 = 普K序列[-2].指标.均线.get(key) - 当前K线.指标.均线[key] = 均线工具.增量EMA(普K序列, period, 配置.指标计算方式, 前值) + for key, 计算方式, ma_type, period in 配置.均线参数列表: + if ma_type == "SMA": + 当前K线.指标.均线[key] = 均线工具.增量SMA(普K序列, period, 计算方式) + elif ma_type == "EMA": + 前值 = None + if len(普K序列) >= 2: + 前值 = 普K序列[-2].指标.均线.get(key) + 当前K线.指标.均线[key] = 均线工具.增量EMA(普K序列, period, 计算方式, 前值) @staticmethod def _回填新指标(全序列: List[K线], 配置: 缠论配置): @@ -2570,16 +2490,14 @@ class 指标计算器: 新参数.append(params) return 新参数 - 新MACD = _新键(尾K指标, 首K指标, 配置._解析MACD参数列表()) - 新RSI = _新键(尾K指标, 首K指标, 配置._解析RSI周期列表()) - 新KDJ = _新键(尾K指标, 首K指标, 配置._解析KDJ参数列表()) - 新BOLL = _新键(尾K指标, 首K指标, 配置._解析BOLL参数列表()) + 新MACD = _新键(尾K指标, 首K指标, 配置.MACD_参数列表) + 新RSI = _新键(尾K指标, 首K指标, 配置.RSI_周期列表) + 新KDJ = _新键(尾K指标, 首K指标, 配置.KDJ_参数列表) + 新BOLL = _新键(尾K指标, 首K指标, 配置.BOLL_参数列表) if not (新MACD or 新RSI or 新KDJ or 新BOLL): return - 计算方式 = 配置.指标计算方式 - for i, k线 in enumerate(全序列): if k线.指标 is None: k线.指标 = 指标容器() @@ -2587,43 +2505,32 @@ class 指标计算器: idx = k线.指标 prev = 全序列[i - 1].指标 if i > 0 else None - for key, 快, 慢, 信号 in 新MACD: + for key, 计算方式, *params in 新MACD: + 快, 慢, 信号 = params[0], params[1], params[2] prev_val = prev[key] if prev is not None and key in prev else None if prev_val is not None: idx[key] = 平滑异同移动平均线.增量计算_K线(prev_val, k线, 计算方式) else: idx[key] = 平滑异同移动平均线.首次计算_K线(k线, 计算方式, 快, 慢, 信号) - for key, 周期 in 新RSI: + for key, 计算方式, *params in 新RSI: + 周期, MA周期, 超买, 超卖 = params[0], params[1], params[2], params[3] prev_val = prev[key] if prev is not None and key in prev else None if prev_val is not None: idx[key] = 相对强弱指数.增量计算_K线(prev_val, k线, 计算方式) else: - idx[key] = 相对强弱指数.首次计算_K线( - k线, - 计算方式, - 周期, - 配置.相对强弱指数_超买阈值, - 配置.相对强弱指数_超卖阈值, - 配置.相对强弱指数_移动平均线周期, - ) + idx[key] = 相对强弱指数.首次计算_K线(k线, 计算方式, 周期, 超买, 超卖, MA周期) - for key, rsv, k平滑, d平滑 in 新KDJ: + for key, 计算方式, *params in 新KDJ: + rsv, k平滑, d平滑, 超买, 超卖 = params[0], params[1], params[2], params[3], params[4] prev_val = prev[key] if prev is not None and key in prev else None if prev_val is not None: idx[key] = 随机指标.增量计算_K线(prev_val, k线, 计算方式) else: - idx[key] = 随机指标.首次计算_K线( - k线, - 计算方式, - rsv, - k平滑, - d平滑, - 配置.随机指标_超买阈值, - 配置.随机指标_超卖阈值, - ) + idx[key] = 随机指标.首次计算_K线(k线, 计算方式, rsv, k平滑, d平滑, 超买, 超卖) - for key, 周期, 标准差倍数 in 新BOLL: + for key, 计算方式, *params in 新BOLL: + 周期, 标准差倍数 = params[0], params[1] prev_val = prev[key] if prev is not None and key in prev else None if prev_val is not None: idx[key] = 布林带.增量计算(prev_val, k线, 计算方式) @@ -4292,23 +4199,24 @@ class 虚线: dif_up = dif_down = dea_up = dea_down = 0 for i in range(1, len(普K序列)): pre, cur = 普K序列[i - 1].macd, 普K序列[i].macd - if pre.DIF is None or cur.DIF is None: + if pre is None or cur is None or pre.DIF is None or cur.DIF is None: continue if pre.DIF < 0 <= cur.DIF: dif_up += 1 elif pre.DIF > 0 >= cur.DIF: dif_down += 1 - if pre.DEA < 0 <= cur.DEA: - dea_up += 1 - elif pre.DEA > 0 >= cur.DEA: - dea_down += 1 + if pre.DEA is not None and cur.DEA is not None: + if pre.DEA < 0 <= cur.DEA: + dea_up += 1 + elif pre.DEA > 0 >= cur.DEA: + dea_down += 1 # 2. DIF与DEA交叉(带标记) golden = death = 0 交叉标记 = [0] # 第0个位置无前值,先填0 for i in range(1, len(普K序列)): pre, cur = 普K序列[i - 1].macd, 普K序列[i].macd - if pre.DIF is None or cur.DIF is None or pre.DEA is None or cur.DEA is None: + if pre is None or cur is None or pre.DIF is None or cur.DIF is None or pre.DEA is None or cur.DEA is None: 交叉标记.append(0) continue if pre.DIF <= pre.DEA and cur.DIF > cur.DEA: @@ -7030,20 +6938,13 @@ class 立体分析器: for 周期 in self.周期组: 临时配置 = 配置组.get(周期, 配置) 当前配置 = 临时配置.model_copy( - update={ - "推送K线": False, - # "推送笔": False, - "推送线段": False, - # "图表展示": False, - }, + update={"图表展示标签": []}, deep=True, ) self._单体分析器[周期] = 观察者(符号=符号, 周期=周期, 配置=当前配置) - self._单体分析器[self.__显示周期].配置.推送K线 = True - self._单体分析器[self.__显示周期].配置.推送笔 = True - self._单体分析器[self.__显示周期].配置.推送线段 = True self._单体分析器[self.__显示周期].配置.图表展示 = True + self._单体分析器[self.__显示周期].配置.图表展示标签 = None # None = 全部展示 self._单体分析器[self.__显示周期].重置基础序列() for 周期 in self.周期组: # 将不同周期对其至显示周期 @@ -7156,7 +7057,7 @@ def 测试_指标挂载(配置: 缠论配置): size = struct.calcsize(">6d") for i in range(len(buffer) // size): if i == 500: - 配置.MACD_参数列表 = [("macd", 配置.平滑异同移动平均线_快线周期, 配置.平滑异同移动平均线_慢线周期, 配置.平滑异同移动平均线_信号周期)] + 配置.MACD_参数列表 = [("macd", "收", 13, 31, 11)] 配置.MACD_参数列表.append(("macd_12_26_9", 12, 26, 9)) k线 = K线.读取大端字节数组(buffer[i * size : i * size + size], 周期, 符号) 观察员.增加原始K线(k线) diff --git a/chanlun-py/Cargo.toml b/chanlun-py/Cargo.toml index abff485..084501b 100644 --- a/chanlun-py/Cargo.toml +++ b/chanlun-py/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chanlun-py" -version = "26.6.77" +version = "26.6.87" edition = "2024" description = "缠论技术分析库 — Rust 高性能 Python 绑定" authors = ["YuYuKunKun"] diff --git a/chanlun-py/chanlun.pyi b/chanlun-py/chanlun.pyi index b70bc78..a29be3a 100644 --- a/chanlun-py/chanlun.pyi +++ b/chanlun-py/chanlun.pyi @@ -886,6 +886,7 @@ class 缠论配置: def from_json(cls, json_str: str) -> 缠论配置: ... @classmethod def 不推送(cls) -> 缠论配置: ... + def 展示标签(self, 标签: str) -> bool: ... @classmethod def 按序号重组字典(cls, 默认配置: Any, 原始字典: Dict[str, Any]) -> Dict[str, Any]: ... def __str__(self) -> str: ... diff --git a/chanlun-py/chanlun/__init__.pyi b/chanlun-py/chanlun/__init__.pyi index b70bc78..a29be3a 100644 --- a/chanlun-py/chanlun/__init__.pyi +++ b/chanlun-py/chanlun/__init__.pyi @@ -886,6 +886,7 @@ class 缠论配置: def from_json(cls, json_str: str) -> 缠论配置: ... @classmethod def 不推送(cls) -> 缠论配置: ... + def 展示标签(self, 标签: str) -> bool: ... @classmethod def 按序号重组字典(cls, 默认配置: Any, 原始字典: Dict[str, Any]) -> Dict[str, Any]: ... def __str__(self) -> str: ... diff --git a/chanlun-py/chanlun/chan.py b/chanlun-py/chanlun/chan.py index 4ffc679..bd3751d 100644 --- a/chanlun-py/chanlun/chan.py +++ b/chanlun-py/chanlun/chan.py @@ -846,7 +846,7 @@ class 缠论配置: **[线段]** 线段_特征序列忽视老阴老阳, 线段_缺口后紧急修正, 线段内部中枢图显 等 \\ **[分析开关]** 分析笔, 分析线段, 分析扩展线段, 分析笔中枢, 分析线段中枢 \\ **[指标]** 计算指标, 指标计算方式, MACD/RSI/KDJ 参数 \\ - **[推送/显示]** 图表展示, 推送K线/笔/线段/中枢 等 \\ + **[推送/显示]** 图表展示 (主开关), 图表展示标签 (标签列表) 等 \\ **[买卖点]** 买卖点偏移, 买卖点激进识别, 买卖点_背离率, 买卖点_计算方式 等 \\ **[背驰]** 线段内部背驰_MACD, 线段内部背驰_斜率 等 \\ **[其他]** 手动终止, 加载文件路径 @@ -885,45 +885,15 @@ class 缠论配置: 分析线段中枢: bool = True, 手动终止: str = "", # 2099-12-31 00:00:00 计算指标: bool = True, - 指标计算方式: str = "收", # (开, 高, 低, 收, 高低均值, 高低收均值, 开高低收均值), 默认 收盘价 - 平滑异同移动平均线_快线周期: int = 13, - 平滑异同移动平均线_慢线周期: int = 31, - 平滑异同移动平均线_信号周期: int = 11, - 相对强弱指数_周期: int = 13, - 相对强弱指数_移动平均线周期: int = 13, - 相对强弱指数_超买阈值: float = 75.0, - 相对强弱指数_超卖阈值: float = 25.0, - 随机指标_RSV周期: int = 13, - 随机指标_K值平滑周期: int = 5, - 随机指标_D值平滑周期: int = 5, - 随机指标_超买阈值: float = 80.0, - 随机指标_超卖阈值: float = 20.0, - 计算BOLL: bool = False, - 布林带_周期: int = 20, - 布林带_标准差倍数: float = 2.0, - 均线_类型列表: List[str] = None, - 均线_周期列表: List[int] = None, + 指标计算方式: str = "收", # 均线计算方式 + 均线参数列表: List[tuple] = None, # [(key, 计算方式, 类型, 周期), ...] # 多参数指标列表(None/空列表 = 使用默认单参数) - MACD_参数列表: List[tuple] = None, # [(key, 快线, 慢线, 信号), ...] - RSI_周期列表: List[tuple] = None, # [(key, 周期), ...] - KDJ_参数列表: List[tuple] = None, # [(key, RSV周期, K平滑, D平滑), ...] - BOLL_参数列表: List[tuple] = None, # [(key, 周期, 标准差倍数), ...] - 图表展示: bool = True, - 推送K线: bool = True, - 推送笔: bool = True, - 推送线段: bool = True, - 推送中枢: bool = True, - 图表展示_笔: bool = True, - 图表展示_线段: bool = True, - 图表展示_扩展线段: bool = True, - 图表展示_扩展线段_线段: bool = True, - 图表展示_线段_线段: bool = True, - 图表展示_中枢_笔: bool = True, - 图表展示_中枢_线段: bool = True, - 图表展示_中枢_扩展线段: bool = True, - 图表展示_中枢_扩展线段_线段: bool = True, - 图表展示_中枢_线段_线段: bool = True, - 图表展示_中枢_线段内部: bool = True, + MACD_参数列表: List[tuple] = None, # [(key, 计算方式, 快线, 慢线, 信号), ...] + RSI_周期列表: List[tuple] = None, # [(key, 计算方式, 周期), ...] + KDJ_参数列表: List[tuple] = None, # [(key, 计算方式, RSV周期, K平滑, D平滑), ...] + BOLL_参数列表: List[tuple] = None, # [(key, 计算方式, 周期, 标准差倍数), ...] + 图表展示: bool = True, # 图表系统主开关 + 图表展示标签: Optional[List[str]] = None, # None=全部展示, []=不展示, [\"笔\",\"线段\"]=指定 买卖点偏移: int = 1, # 最大偏移 买卖点激进识别: bool = False, # 激进模式下将不考虑分型的完整性 买卖点与MACD柱强相关: bool = False, # True: 卖点需正值 买点需负值 @@ -963,43 +933,13 @@ class 缠论配置: self.手动终止 = 手动终止 self.计算指标 = 计算指标 self.指标计算方式 = 指标计算方式 - self.平滑异同移动平均线_快线周期 = 平滑异同移动平均线_快线周期 - self.平滑异同移动平均线_慢线周期 = 平滑异同移动平均线_慢线周期 - self.平滑异同移动平均线_信号周期 = 平滑异同移动平均线_信号周期 - self.相对强弱指数_周期 = 相对强弱指数_周期 - self.相对强弱指数_移动平均线周期 = 相对强弱指数_移动平均线周期 - self.相对强弱指数_超买阈值 = 相对强弱指数_超买阈值 - self.相对强弱指数_超卖阈值 = 相对强弱指数_超卖阈值 - self.随机指标_RSV周期 = 随机指标_RSV周期 - self.随机指标_K值平滑周期 = 随机指标_K值平滑周期 - self.随机指标_D值平滑周期 = 随机指标_D值平滑周期 - self.随机指标_超买阈值 = 随机指标_超买阈值 - self.随机指标_超卖阈值 = 随机指标_超卖阈值 - self.计算BOLL = 计算BOLL - self.布林带_周期 = 布林带_周期 - self.布林带_标准差倍数 = 布林带_标准差倍数 - self.均线_类型列表 = 均线_类型列表 if 均线_类型列表 is not None else [] - self.均线_周期列表 = 均线_周期列表 if 均线_周期列表 is not None else [] - self.MACD_参数列表 = MACD_参数列表 if MACD_参数列表 is not None else [] - self.RSI_周期列表 = RSI_周期列表 if RSI_周期列表 is not None else [] - self.KDJ_参数列表 = KDJ_参数列表 if KDJ_参数列表 is not None else [] - self.BOLL_参数列表 = BOLL_参数列表 if BOLL_参数列表 is not None else [] + self.均线参数列表 = 均线参数列表 if 均线参数列表 is not None else [] + self.MACD_参数列表 = MACD_参数列表 if MACD_参数列表 is not None else [("macd", "收", 13, 31, 11)] + self.RSI_周期列表 = RSI_周期列表 if RSI_周期列表 is not None else [("rsi", "收", 14, 13, 75.0, 25.0)] + self.KDJ_参数列表 = KDJ_参数列表 if KDJ_参数列表 is not None else [("kdj", "收", 13, 5, 5, 80.0, 20.0)] + self.BOLL_参数列表 = BOLL_参数列表 if BOLL_参数列表 is not None else [("boll", "收", 20, 2.0)] self.图表展示 = 图表展示 - self.推送K线 = 推送K线 - self.推送笔 = 推送笔 - self.推送线段 = 推送线段 - self.推送中枢 = 推送中枢 - self.图表展示_笔 = 图表展示_笔 - self.图表展示_线段 = 图表展示_线段 - self.图表展示_扩展线段 = 图表展示_扩展线段 - self.图表展示_扩展线段_线段 = 图表展示_扩展线段_线段 - self.图表展示_线段_线段 = 图表展示_线段_线段 - self.图表展示_中枢_笔 = 图表展示_中枢_笔 - self.图表展示_中枢_线段 = 图表展示_中枢_线段 - self.图表展示_中枢_扩展线段 = 图表展示_中枢_扩展线段 - self.图表展示_中枢_扩展线段_线段 = 图表展示_中枢_扩展线段_线段 - self.图表展示_中枢_线段_线段 = 图表展示_中枢_线段_线段 - self.图表展示_中枢_线段内部 = 图表展示_中枢_线段内部 + self.图表展示标签 = set(图表展示标签) if 图表展示标签 is not None else None self.买卖点偏移 = 买卖点偏移 self.买卖点激进识别 = 买卖点激进识别 self.买卖点与MACD柱强相关 = 买卖点与MACD柱强相关 @@ -1049,43 +989,13 @@ class 缠论配置: "手动终止": {"annotation": str, "default": ""}, "计算指标": {"annotation": bool, "default": True}, "指标计算方式": {"annotation": str, "default": "收"}, - "平滑异同移动平均线_快线周期": {"annotation": int, "default": 13}, - "平滑异同移动平均线_慢线周期": {"annotation": int, "default": 31}, - "平滑异同移动平均线_信号周期": {"annotation": int, "default": 11}, - "相对强弱指数_周期": {"annotation": int, "default": 13}, - "相对强弱指数_移动平均线周期": {"annotation": int, "default": 13}, - "相对强弱指数_超买阈值": {"annotation": float, "default": 75.0}, - "相对强弱指数_超卖阈值": {"annotation": float, "default": 25.0}, - "随机指标_RSV周期": {"annotation": int, "default": 13}, - "随机指标_K值平滑周期": {"annotation": int, "default": 5}, - "随机指标_D值平滑周期": {"annotation": int, "default": 5}, - "随机指标_超买阈值": {"annotation": float, "default": 80.0}, - "随机指标_超卖阈值": {"annotation": float, "default": 20.0}, - "计算BOLL": {"annotation": bool, "default": False}, - "布林带_周期": {"annotation": int, "default": 20}, - "布林带_标准差倍数": {"annotation": float, "default": 2.0}, - "均线_类型列表": {"annotation": List[str], "default": []}, - "均线_周期列表": {"annotation": List[int], "default": []}, - "MACD_参数列表": {"annotation": List[tuple], "default": []}, - "RSI_周期列表": {"annotation": List[tuple], "default": []}, - "KDJ_参数列表": {"annotation": List[tuple], "default": []}, - "BOLL_参数列表": {"annotation": List[tuple], "default": []}, + "均线参数列表": {"annotation": List[tuple], "default": []}, + "MACD_参数列表": {"annotation": List[tuple], "default": [("macd", "收", 13, 31, 11)]}, + "RSI_周期列表": {"annotation": List[tuple], "default": [("rsi", "收", 14, 13, 75.0, 25.0)]}, + "KDJ_参数列表": {"annotation": List[tuple], "default": [("kdj", "收", 13, 5, 5, 80.0, 20.0)]}, + "BOLL_参数列表": {"annotation": List[tuple], "default": [("boll", "收", 20, 2.0)]}, "图表展示": {"annotation": bool, "default": True}, - "推送K线": {"annotation": bool, "default": True}, - "推送笔": {"annotation": bool, "default": True}, - "推送线段": {"annotation": bool, "default": True}, - "推送中枢": {"annotation": bool, "default": True}, - "图表展示_笔": {"annotation": bool, "default": True}, - "图表展示_线段": {"annotation": bool, "default": True}, - "图表展示_扩展线段": {"annotation": bool, "default": True}, - "图表展示_扩展线段_线段": {"annotation": bool, "default": True}, - "图表展示_线段_线段": {"annotation": bool, "default": True}, - "图表展示_中枢_笔": {"annotation": bool, "default": True}, - "图表展示_中枢_线段": {"annotation": bool, "default": True}, - "图表展示_中枢_扩展线段": {"annotation": bool, "default": True}, - "图表展示_中枢_扩展线段_线段": {"annotation": bool, "default": True}, - "图表展示_中枢_线段_线段": {"annotation": bool, "default": True}, - "图表展示_中枢_线段内部": {"annotation": bool, "default": True}, + "图表展示标签": {"annotation": Optional[List[str]], "default": None}, "买卖点偏移": {"annotation": int, "default": 1}, "买卖点激进识别": {"annotation": bool, "default": False}, "买卖点与MACD柱强相关": {"annotation": bool, "default": False}, @@ -1137,27 +1047,45 @@ class 缠论配置: logger.warning(f"[{fname}] = {value} 解析失败,使用默认值:{default}") setattr(self, fname, default) - # ---- 参数列表解析:多参数列表为空时回退到默认单参数 ---- + def 设置指标( + self, + *, + 均线: List[tuple] = None, + MACD: List[tuple] = None, + RSI: List[tuple] = None, + KDJ: List[tuple] = None, + BOLL: List[tuple] = None, + ): + """统一设置所有指标参数。 - def _解析MACD参数列表(self) -> List[tuple]: - if self.MACD_参数列表: - return self.MACD_参数列表 - return [("macd", self.平滑异同移动平均线_快线周期, self.平滑异同移动平均线_慢线周期, self.平滑异同移动平均线_信号周期)] + 元组格式 ``(key, 计算方式, *params)``: + - 均线: ``("SMA_5", "收", "SMA", 5)`` — key/计算方式/类型/周期 + - MACD: ``("默认", "收", 13, 31, 11)`` — 快线/慢线/信号 + - RSI: ``("默认", "收", 14, 13, 75, 25)`` — 周期/MA周期/超买/超卖 + - KDJ: ``("默认", "收", 13, 5, 5, 80, 20)`` — RSV/K平滑/D平滑/超买/超卖 + - BOLL: ``("默认", "收", 20, 2.0)`` — 周期/标准差倍数 - def _解析RSI周期列表(self) -> List[tuple]: - if self.RSI_周期列表: - return self.RSI_周期列表 - return [("rsi", self.相对强弱指数_周期)] + :param 均线: key 即均线名(如 ``"SMA_5"``),同时编码类型和周期 + :param MACD: 首个 key 同时写入 ``"macd"`` 兼容槽位 + :param BOLL: BOLL 参数元组列表,为空则不计算 + """ + self.计算指标 = True + if 均线 is not None: + self.均线参数列表 = 均线 + if MACD is not None: + self.MACD_参数列表 = MACD + if RSI is not None: + self.RSI_周期列表 = RSI + if KDJ is not None: + self.KDJ_参数列表 = KDJ + if BOLL is not None: + self.BOLL_参数列表 = BOLL - def _解析KDJ参数列表(self) -> List[tuple]: - if self.KDJ_参数列表: - return self.KDJ_参数列表 - return [("kdj", self.随机指标_RSV周期, self.随机指标_K值平滑周期, self.随机指标_D值平滑周期)] - - def _解析BOLL参数列表(self) -> List[tuple]: - if self.BOLL_参数列表: - return self.BOLL_参数列表 - return [("boll", self.布林带_周期, self.布林带_标准差倍数)] + def 展示标签(self, 标签: str) -> bool: + """判断指定标签是否应展示。None = 全部展示,空列表 = 全部隐藏。""" + if self.图表展示标签 is None: + return True + return 标签 in self.图表展示标签 @classmethod def 兼容旧版本配置(cls, values: Dict[str, Any]) -> Dict[str, Any]: @@ -1190,7 +1118,13 @@ class 缠论配置: :return: 包含所有配置字段的字典 """ - return {k: getattr(self, k) for k in self.model_fields().keys()} + result = {} + for k in self.model_fields().keys(): + v = getattr(self, k) + if isinstance(v, set): + v = list(v) + result[k] = v + return result def to_json(self) -> str: """将配置序列化为 JSON 字符串。 @@ -1246,21 +1180,7 @@ class 缠论配置: return cls( 线段内部中枢图显=False, 图表展示=False, - 推送K线=False, - 推送笔=False, - 推送线段=False, - 推送中枢=False, - 图表展示_笔=False, - 图表展示_线段=False, - 图表展示_扩展线段=False, - 图表展示_扩展线段_线段=False, - 图表展示_线段_线段=False, - 图表展示_中枢_笔=False, - 图表展示_中枢_线段=False, - 图表展示_中枢_扩展线段=False, - 图表展示_中枢_扩展线段_线段=False, - 图表展示_中枢_线段_线段=False, - 图表展示_中枢_线段内部=False, + 图表展示标签=[], ) def model_copy(self, update: dict = None, deep: bool = True): @@ -2479,19 +2399,21 @@ class 指标计算器: @staticmethod def _计算MACD组(当前K线: K线, prev: Optional[指标容器], 配置: 缠论配置): idx = 当前K线.指标 - 计算方式 = 配置.指标计算方式 - for key, 快, 慢, 信号 in 配置._解析MACD参数列表(): + for i, (key, 计算方式, *params) in enumerate(配置.MACD_参数列表): + 快, 慢, 信号 = params[0], params[1], params[2] prev_val = prev[key] if prev is not None and key in prev else None if prev_val is not None: idx[key] = 平滑异同移动平均线.增量计算_K线(prev_val, 当前K线, 计算方式) else: idx[key] = 平滑异同移动平均线.首次计算_K线(当前K线, 计算方式, 快, 慢, 信号) + if i == 0: + idx["macd"] = idx[key] @staticmethod def _计算RSI组(当前K线: K线, prev: Optional[指标容器], 配置: 缠论配置): idx = 当前K线.指标 - 计算方式 = 配置.指标计算方式 - for key, 周期 in 配置._解析RSI周期列表(): + for key, 计算方式, *params in 配置.RSI_周期列表: + 周期, MA周期, 超买, 超卖 = params[0], params[1], params[2], params[3] prev_val = prev[key] if prev is not None and key in prev else None if prev_val is not None: idx[key] = 相对强弱指数.增量计算_K线(prev_val, 当前K线, 计算方式) @@ -2500,16 +2422,16 @@ class 指标计算器: 当前K线, 计算方式, 周期, - 配置.相对强弱指数_超买阈值, - 配置.相对强弱指数_超卖阈值, - 配置.相对强弱指数_移动平均线周期, + 超买, + 超卖, + MA周期, ) @staticmethod def _计算KDJ组(当前K线: K线, prev: Optional[指标容器], 配置: 缠论配置): idx = 当前K线.指标 - 计算方式 = 配置.指标计算方式 - for key, rsv, k平滑, d平滑 in 配置._解析KDJ参数列表(): + for key, 计算方式, *params in 配置.KDJ_参数列表: + rsv, k平滑, d平滑, 超买, 超卖 = params[0], params[1], params[2], params[3], params[4] prev_val = prev[key] if prev is not None and key in prev else None if prev_val is not None: idx[key] = 随机指标.增量计算_K线(prev_val, 当前K线, 计算方式) @@ -2520,15 +2442,15 @@ class 指标计算器: rsv, k平滑, d平滑, - 配置.随机指标_超买阈值, - 配置.随机指标_超卖阈值, + 超买, + 超卖, ) @staticmethod def _计算BOLL组(当前K线: K线, prev: Optional[指标容器], 配置: 缠论配置): idx = 当前K线.指标 - 计算方式 = 配置.指标计算方式 - for key, 周期, 标准差倍数 in 配置._解析BOLL参数列表(): + for key, 计算方式, *params in 配置.BOLL_参数列表: + 周期, 标准差倍数 = params[0], params[1] prev_val = prev[key] if prev is not None and key in prev else None if prev_val is not None: idx[key] = 布林带.增量计算(prev_val, 当前K线, 计算方式) @@ -2537,18 +2459,16 @@ class 指标计算器: @staticmethod def _更新均线(当前K线: K线, 普K序列: List[K线], 配置: 缠论配置): - if not 配置.均线_类型列表 or not 配置.均线_周期列表: + if not 配置.均线参数列表: return - for ma_type in 配置.均线_类型列表: - for period in 配置.均线_周期列表: - key = f"{ma_type}_{period}" - if ma_type == "SMA": - 当前K线.指标.均线[key] = 均线工具.增量SMA(普K序列, period, 配置.指标计算方式) - elif ma_type == "EMA": - 前值 = None - if len(普K序列) >= 2: - 前值 = 普K序列[-2].指标.均线.get(key) - 当前K线.指标.均线[key] = 均线工具.增量EMA(普K序列, period, 配置.指标计算方式, 前值) + for key, 计算方式, ma_type, period in 配置.均线参数列表: + if ma_type == "SMA": + 当前K线.指标.均线[key] = 均线工具.增量SMA(普K序列, period, 计算方式) + elif ma_type == "EMA": + 前值 = None + if len(普K序列) >= 2: + 前值 = 普K序列[-2].指标.均线.get(key) + 当前K线.指标.均线[key] = 均线工具.增量EMA(普K序列, period, 计算方式, 前值) @staticmethod def _回填新指标(全序列: List[K线], 配置: 缠论配置): @@ -2570,16 +2490,14 @@ class 指标计算器: 新参数.append(params) return 新参数 - 新MACD = _新键(尾K指标, 首K指标, 配置._解析MACD参数列表()) - 新RSI = _新键(尾K指标, 首K指标, 配置._解析RSI周期列表()) - 新KDJ = _新键(尾K指标, 首K指标, 配置._解析KDJ参数列表()) - 新BOLL = _新键(尾K指标, 首K指标, 配置._解析BOLL参数列表()) + 新MACD = _新键(尾K指标, 首K指标, 配置.MACD_参数列表) + 新RSI = _新键(尾K指标, 首K指标, 配置.RSI_周期列表) + 新KDJ = _新键(尾K指标, 首K指标, 配置.KDJ_参数列表) + 新BOLL = _新键(尾K指标, 首K指标, 配置.BOLL_参数列表) if not (新MACD or 新RSI or 新KDJ or 新BOLL): return - 计算方式 = 配置.指标计算方式 - for i, k线 in enumerate(全序列): if k线.指标 is None: k线.指标 = 指标容器() @@ -2587,43 +2505,32 @@ class 指标计算器: idx = k线.指标 prev = 全序列[i - 1].指标 if i > 0 else None - for key, 快, 慢, 信号 in 新MACD: + for key, 计算方式, *params in 新MACD: + 快, 慢, 信号 = params[0], params[1], params[2] prev_val = prev[key] if prev is not None and key in prev else None if prev_val is not None: idx[key] = 平滑异同移动平均线.增量计算_K线(prev_val, k线, 计算方式) else: idx[key] = 平滑异同移动平均线.首次计算_K线(k线, 计算方式, 快, 慢, 信号) - for key, 周期 in 新RSI: + for key, 计算方式, *params in 新RSI: + 周期, MA周期, 超买, 超卖 = params[0], params[1], params[2], params[3] prev_val = prev[key] if prev is not None and key in prev else None if prev_val is not None: idx[key] = 相对强弱指数.增量计算_K线(prev_val, k线, 计算方式) else: - idx[key] = 相对强弱指数.首次计算_K线( - k线, - 计算方式, - 周期, - 配置.相对强弱指数_超买阈值, - 配置.相对强弱指数_超卖阈值, - 配置.相对强弱指数_移动平均线周期, - ) + idx[key] = 相对强弱指数.首次计算_K线(k线, 计算方式, 周期, 超买, 超卖, MA周期) - for key, rsv, k平滑, d平滑 in 新KDJ: + for key, 计算方式, *params in 新KDJ: + rsv, k平滑, d平滑, 超买, 超卖 = params[0], params[1], params[2], params[3], params[4] prev_val = prev[key] if prev is not None and key in prev else None if prev_val is not None: idx[key] = 随机指标.增量计算_K线(prev_val, k线, 计算方式) else: - idx[key] = 随机指标.首次计算_K线( - k线, - 计算方式, - rsv, - k平滑, - d平滑, - 配置.随机指标_超买阈值, - 配置.随机指标_超卖阈值, - ) + idx[key] = 随机指标.首次计算_K线(k线, 计算方式, rsv, k平滑, d平滑, 超买, 超卖) - for key, 周期, 标准差倍数 in 新BOLL: + for key, 计算方式, *params in 新BOLL: + 周期, 标准差倍数 = params[0], params[1] prev_val = prev[key] if prev is not None and key in prev else None if prev_val is not None: idx[key] = 布林带.增量计算(prev_val, k线, 计算方式) @@ -4292,23 +4199,24 @@ class 虚线: dif_up = dif_down = dea_up = dea_down = 0 for i in range(1, len(普K序列)): pre, cur = 普K序列[i - 1].macd, 普K序列[i].macd - if pre.DIF is None or cur.DIF is None: + if pre is None or cur is None or pre.DIF is None or cur.DIF is None: continue if pre.DIF < 0 <= cur.DIF: dif_up += 1 elif pre.DIF > 0 >= cur.DIF: dif_down += 1 - if pre.DEA < 0 <= cur.DEA: - dea_up += 1 - elif pre.DEA > 0 >= cur.DEA: - dea_down += 1 + if pre.DEA is not None and cur.DEA is not None: + if pre.DEA < 0 <= cur.DEA: + dea_up += 1 + elif pre.DEA > 0 >= cur.DEA: + dea_down += 1 # 2. DIF与DEA交叉(带标记) golden = death = 0 交叉标记 = [0] # 第0个位置无前值,先填0 for i in range(1, len(普K序列)): pre, cur = 普K序列[i - 1].macd, 普K序列[i].macd - if pre.DIF is None or cur.DIF is None or pre.DEA is None or cur.DEA is None: + if pre is None or cur is None or pre.DIF is None or cur.DIF is None or pre.DEA is None or cur.DEA is None: 交叉标记.append(0) continue if pre.DIF <= pre.DEA and cur.DIF > cur.DEA: @@ -7030,20 +6938,13 @@ class 立体分析器: for 周期 in self.周期组: 临时配置 = 配置组.get(周期, 配置) 当前配置 = 临时配置.model_copy( - update={ - "推送K线": False, - # "推送笔": False, - "推送线段": False, - # "图表展示": False, - }, + update={"图表展示标签": []}, deep=True, ) self._单体分析器[周期] = 观察者(符号=符号, 周期=周期, 配置=当前配置) - self._单体分析器[self.__显示周期].配置.推送K线 = True - self._单体分析器[self.__显示周期].配置.推送笔 = True - self._单体分析器[self.__显示周期].配置.推送线段 = True self._单体分析器[self.__显示周期].配置.图表展示 = True + self._单体分析器[self.__显示周期].配置.图表展示标签 = None # None = 全部展示 self._单体分析器[self.__显示周期].重置基础序列() for 周期 in self.周期组: # 将不同周期对其至显示周期 @@ -7156,7 +7057,7 @@ def 测试_指标挂载(配置: 缠论配置): size = struct.calcsize(">6d") for i in range(len(buffer) // size): if i == 500: - 配置.MACD_参数列表 = [("macd", 配置.平滑异同移动平均线_快线周期, 配置.平滑异同移动平均线_慢线周期, 配置.平滑异同移动平均线_信号周期)] + 配置.MACD_参数列表 = [("macd", "收", 13, 31, 11)] 配置.MACD_参数列表.append(("macd_12_26_9", 12, 26, 9)) k线 = K线.读取大端字节数组(buffer[i * size : i * size + size], 周期, 符号) 观察员.增加原始K线(k线) diff --git a/chanlun-py/pyproject.toml b/chanlun-py/pyproject.toml index b271448..ce1d32c 100644 --- a/chanlun-py/pyproject.toml +++ b/chanlun-py/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "chanlun" -version = "2606.77" +version = "2606.87" description = "缠论技术分析库 — Rust 高性能实现" readme = { file = "README.md", content-type = "text/markdown" } license = { file = "LICENSE", content-type = "text/plain" } diff --git a/chanlun-py/src/config_py.rs b/chanlun-py/src/config_py.rs index 8e128ff..536496b 100644 --- a/chanlun-py/src/config_py.rs +++ b/chanlun-py/src/config_py.rs @@ -252,6 +252,11 @@ impl 缠论配置Py { }) } + /// 判断指定标签是否应展示。None = 全部展示,空列表 = 全部隐藏。 + fn 展示标签(&self, 标签: &str) -> bool { + self.缓存.lock().as_ref().map_or(true, |c| c.展示标签(标签)) + } + #[classmethod] /// 将形如 "1_open", "1_close", "2_open", "name" 的字典重组为嵌套结构 fn 按序号重组字典( diff --git a/chanlun/src/business/multi_frame.rs b/chanlun/src/business/multi_frame.rs index 17be222..ffd35b8 100644 --- a/chanlun/src/business/multi_frame.rs +++ b/chanlun/src/business/multi_frame.rs @@ -61,8 +61,7 @@ impl 立体分析器 { .get(&周期) .cloned() .unwrap_or_else(|| 默认配置.clone()); - 当前配置.推送K线 = false; - 当前配置.推送线段 = false; + 当前配置.图表展示标签 = Some(vec![]); 当前配置.标识 = 符号.clone(); let 观察员 = 观察者::new(符号.clone(), 周期, 当前配置); @@ -73,9 +72,7 @@ impl 立体分析器 { { let 显示观察员 = 单体分析器.get(&显示周期).expect("显示周期观察者不存在"); let mut guard = 显示观察员.write(); - guard.配置.推送K线 = true; - guard.配置.推送笔 = true; - guard.配置.推送线段 = true; + guard.配置.图表展示标签 = None; // None = 全部展示 guard.配置.图表展示 = true; guard.重置基础序列(); } diff --git a/chanlun/src/config.rs b/chanlun/src/config.rs index fd4dce6..23a5386 100644 --- a/chanlun/src/config.rs +++ b/chanlun/src/config.rs @@ -92,103 +92,34 @@ pub struct 缠论配置 { // ---- 指标 ---- /// 是否计算技术指标 pub 计算指标: bool, - /// 是否计算布林带 - pub 计算BOLL: bool, - /// 指标计算方式(开/高/低/收/高低均值/高低收均值/开高低收均值) + /// 指标计算方式(均线使用,MACD/RSI/KDJ/BOLL 在参数元组中指定) #[serde(deserialize_with = "deserialize_指标计算方式")] pub 指标计算方式: String, - // ---- MACD ---- - /// MACD 快线 EMA 周期 - pub 平滑异同移动平均线_快线周期: i64, - /// MACD 慢线 EMA 周期 - pub 平滑异同移动平均线_慢线周期: i64, - /// MACD 信号线周期 - pub 平滑异同移动平均线_信号周期: i64, - /// MACD 多参数列表: Vec<(key, 快线, 慢线, 信号)> + /// MACD 参数列表 (key, 计算方式, 快线, 慢线, 信号) #[serde(default)] - pub MACD_参数列表: Vec<(String, i64, i64, i64)>, + pub MACD_参数列表: Vec<(String, String, i64, i64, i64)>, - // ---- RSI ---- - /// RSI 计算周期 - pub 相对强弱指数_周期: i64, - /// RSI SMA 平滑周期 - pub 相对强弱指数_移动平均线周期: i64, - /// RSI 超买阈值 - pub 相对强弱指数_超买阈值: f64, - /// RSI 超卖阈值 - pub 相对强弱指数_超卖阈值: f64, - /// RSI 多周期列表: Vec<(key, 周期)> + /// RSI 参数列表 (key, 计算方式, 周期, MA周期, 超买, 超卖) #[serde(default)] - pub RSI_周期列表: Vec<(String, i64)>, + pub RSI_周期列表: Vec<(String, String, i64, i64, f64, f64)>, - // ---- KDJ ---- - /// KDJ RSV 周期 - pub 随机指标_RSV周期: i64, - /// KDJ K 值平滑周期 - pub 随机指标_K值平滑周期: i64, - /// KDJ D 值平滑周期 - pub 随机指标_D值平滑周期: i64, - /// KDJ 超买阈值 - pub 随机指标_超买阈值: f64, - /// KDJ 超卖阈值 - pub 随机指标_超卖阈值: f64, - /// KDJ 多参数列表: Vec<(key, RSV周期, K平滑, D平滑)> + /// KDJ 参数列表 (key, 计算方式, RSV, K平滑, D平滑, 超买, 超卖) #[serde(default)] - pub KDJ_参数列表: Vec<(String, i64, i64, i64)>, + pub KDJ_参数列表: Vec<(String, String, i64, i64, i64, f64, f64)>, - // ---- BOLL ---- - /// 布林带周期 - pub 布林带_周期: i64, - /// 布林带标准差倍数 - pub 布林带_标准差倍数: f64, - /// BOLL 多参数列表: Vec<(key, 周期, 标准差倍数)> + /// BOLL 参数列表 (key, 计算方式, 周期, 标准差倍数) #[serde(default)] - pub BOLL_参数列表: Vec<(String, i64, f64)>, + pub BOLL_参数列表: Vec<(String, String, i64, f64)>, - // ---- 均线 ---- - /// 均线类型列表: ["SMA", "EMA", ...] + /// 均线参数列表 (key, 计算方式, 类型, 周期) — 如 ("SMA_5", "收", "SMA", 5) #[serde(default)] - pub 均线_类型列表: Vec, - /// 均线周期列表: [5, 10, 20, ...] - #[serde(default)] - pub 均线_周期列表: Vec, + pub 均线参数列表: Vec<(String, String, String, i64)>, // ---- 推送/显示 ---- - /// 是否启用图表展示 pub 图表展示: bool, - /// 是否推送K线 - pub 推送K线: bool, - /// 是否推送笔 - pub 推送笔: bool, - /// 是否推送线段 - pub 推送线段: bool, - /// 是否推送中枢 - pub 推送中枢: bool, - - // ---- 图表展示细分 ---- - /// 图表展示笔 - pub 图表展示_笔: bool, - /// 图表展示线段 - pub 图表展示_线段: bool, - /// 图表展示扩展线段 - pub 图表展示_扩展线段: bool, - /// 图表展示扩展线段(线段级) - pub 图表展示_扩展线段_线段: bool, - /// 图表展示线段之线段 - pub 图表展示_线段_线段: bool, - /// 图表展示笔中枢 - pub 图表展示_中枢_笔: bool, - /// 图表展示线段中枢 - pub 图表展示_中枢_线段: bool, - /// 图表展示扩展中枢 - pub 图表展示_中枢_扩展线段: bool, - /// 图表展示扩展中枢(线段级) - pub 图表展示_中枢_扩展线段_线段: bool, - /// 图表展示线段之中枢 - pub 图表展示_中枢_线段_线段: bool, - /// 图表展示线段内部中枢 - pub 图表展示_中枢_线段内部: bool, + /// 图表展示标签: None=全部, [] = 不展示 + pub 图表展示标签: Option>, // ---- 买卖点 ---- /// 买卖点偏移量 @@ -310,44 +241,14 @@ impl Default for 缠论配置 { 分析线段中枢: true, 手动终止: String::new(), 计算指标: true, - 计算BOLL: false, 指标计算方式: "收".into(), - 平滑异同移动平均线_快线周期: 13, - 平滑异同移动平均线_慢线周期: 31, - 平滑异同移动平均线_信号周期: 11, - 相对强弱指数_周期: 13, - 相对强弱指数_移动平均线周期: 13, - 相对强弱指数_超买阈值: 75.0, - 相对强弱指数_超卖阈值: 25.0, - 随机指标_RSV周期: 13, - 随机指标_K值平滑周期: 5, - 随机指标_D值平滑周期: 5, - 随机指标_超买阈值: 80.0, - 随机指标_超卖阈值: 20.0, - MACD_参数列表: Vec::new(), - RSI_周期列表: Vec::new(), - KDJ_参数列表: Vec::new(), - 布林带_周期: 20, - 布林带_标准差倍数: 2.0, - BOLL_参数列表: Vec::new(), - 均线_类型列表: Vec::new(), - 均线_周期列表: Vec::new(), + MACD_参数列表: vec![("macd".into(), "收".into(), 13, 31, 11)], + RSI_周期列表: vec![("rsi".into(), "收".into(), 14, 13, 75.0, 25.0)], + KDJ_参数列表: vec![("kdj".into(), "收".into(), 13, 5, 5, 80.0, 20.0)], + BOLL_参数列表: vec![("boll".into(), "收".into(), 20, 2.0)], + 均线参数列表: Vec::new(), 图表展示: true, - 推送K线: true, - 推送笔: true, - 推送线段: true, - 推送中枢: true, - 图表展示_笔: true, - 图表展示_线段: true, - 图表展示_扩展线段: true, - 图表展示_扩展线段_线段: true, - 图表展示_线段_线段: true, - 图表展示_中枢_笔: true, - 图表展示_中枢_线段: true, - 图表展示_中枢_扩展线段: true, - 图表展示_中枢_扩展线段_线段: true, - 图表展示_中枢_线段_线段: true, - 图表展示_中枢_线段内部: true, + 图表展示标签: None, 买卖点偏移: 1, 买卖点激进识别: false, 买卖点与MACD柱强相关: false, @@ -366,46 +267,12 @@ impl Default for 缠论配置 { } impl 缠论配置 { - /// 解析MACD参数列表 — 如果列表非空则使用列表,否则返回默认单组 - pub fn _解析MACD参数列表(&self) -> Vec<(String, i64, i64, i64)> { - if !self.MACD_参数列表.is_empty() { - return self.MACD_参数列表.clone(); + /// 展示标签判定 — None=全部, [] = 全关 + pub fn 展示标签(&self, 标签: &str) -> bool { + match &self.图表展示标签 { + None => true, + Some(tags) => tags.iter().any(|t| t == 标签), } - vec![( - "macd".into(), - self.平滑异同移动平均线_快线周期, - self.平滑异同移动平均线_慢线周期, - self.平滑异同移动平均线_信号周期, - )] - } - - /// 解析RSI周期列表 — 如果列表非空则使用列表,否则返回默认单组 - pub fn _解析RSI周期列表(&self) -> Vec<(String, i64)> { - if !self.RSI_周期列表.is_empty() { - return self.RSI_周期列表.clone(); - } - vec![("rsi".into(), self.相对强弱指数_周期)] - } - - /// 解析KDJ参数列表 — 如果列表非空则使用列表,否则返回默认单组 - pub fn _解析KDJ参数列表(&self) -> Vec<(String, i64, i64, i64)> { - if !self.KDJ_参数列表.is_empty() { - return self.KDJ_参数列表.clone(); - } - vec![( - "kdj".into(), - self.随机指标_RSV周期, - self.随机指标_K值平滑周期, - self.随机指标_D值平滑周期, - )] - } - - /// 解析BOLL参数列表 — 如果列表非空则使用列表,否则返回默认单组 - pub fn _解析BOLL参数列表(&self) -> Vec<(String, i64, f64)> { - if !self.BOLL_参数列表.is_empty() { - return self.BOLL_参数列表.clone(); - } - vec![("boll".into(), self.布林带_周期, self.布林带_标准差倍数)] } /// 序列化为 JSON 字典(对应 Python to_dict,仅返回 model_fields 中的字段) @@ -576,21 +443,7 @@ impl 缠论配置 { Self { 线段内部中枢图显: false, 图表展示: false, - 推送K线: false, - 推送笔: false, - 推送线段: false, - 推送中枢: false, - 图表展示_笔: false, - 图表展示_线段: false, - 图表展示_扩展线段: false, - 图表展示_扩展线段_线段: false, - 图表展示_线段_线段: false, - 图表展示_中枢_笔: false, - 图表展示_中枢_线段: false, - 图表展示_中枢_扩展线段: false, - 图表展示_中枢_扩展线段_线段: false, - 图表展示_中枢_线段_线段: false, - 图表展示_中枢_线段内部: false, + 图表展示标签: Some(vec![]), ..self.clone() } } diff --git a/chanlun/src/indicators/calculator.rs b/chanlun/src/indicators/calculator.rs index 1e0f706..3fcffc0 100644 --- a/chanlun/src/indicators/calculator.rs +++ b/chanlun/src/indicators/calculator.rs @@ -56,7 +56,7 @@ impl 指标计算器 { Self::_计算KDJ组(当前K线, prev, 配置); Self::_计算BOLL组(当前K线, prev, 配置); } - Self::_更新均线(当前K线, 现有序列, 配置); + Self::_更新均线(当前K线, 现有序列, 配置); // 当前K线在 全序列[n-1], 现有序列不含它 has_prev = n > 1; } // prev_guard dropped here @@ -66,10 +66,9 @@ impl 指标计算器 { } fn _计算MACD组(当前K线: &K线, prev: Option<&指标容器>, 配置: &缠论配置) { - let 计算方式 = &配置.指标计算方式; - for (i, (key, 快, 慢, 信号)) in 配置._解析MACD参数列表().into_iter().enumerate() + for (i, (key, 计算方式, 快, 慢, 信号)) in 配置.MACD_参数列表.iter().enumerate() { - let val = if let Some(prev_val) = prev.and_then(|p| p.获取(&key)) { + let val = if let Some(prev_val) = prev.and_then(|p| p.获取(key)) { if let 指标值::MACD(prev_macd) = prev_val { 指标值::MACD(平滑异同移动平均线::增量计算( prev_macd, @@ -95,21 +94,18 @@ impl 指标计算器 { 计算方式, ), 当前K线.时间戳, - 快, - 慢, - 信号, + *快, + *慢, + *信号, )) }; - 当前K线.指标.write().设置(&key, val.clone()); - if i == 0 { - 当前K线.指标.write().设置("macd", val); - } + 当前K线.指标.write().设置(key, val.clone()); } } fn _计算RSI组(当前K线: &K线, prev: Option<&指标容器>, 配置: &缠论配置) { - let 计算方式 = &配置.指标计算方式; - for (i, (key, 周期)) in 配置._解析RSI周期列表().into_iter().enumerate() { + for (key, 计算方式, 周期, ma周期, 超买, 超卖) in 配置.RSI_周期列表.iter() + { let val = if let Some(prev_val) = prev.and_then(|p| p.获取(&key)) { if let 指标值::RSI(prev_rsi) = prev_val { 指标值::RSI(相对强弱指数::增量计算( @@ -136,23 +132,19 @@ impl 指标计算器 { 计算方式, ), 当前K线.时间戳, - 周期, - 配置.相对强弱指数_超买阈值, - 配置.相对强弱指数_超卖阈值, - Some(配置.相对强弱指数_移动平均线周期), + *周期, + *超买, + *超卖, + Some(*ma周期), )) }; 当前K线.指标.write().设置(&key, val.clone()); - if i == 0 { - 当前K线.指标.write().设置("rsi", val); - } } } fn _计算KDJ组(当前K线: &K线, prev: Option<&指标容器>, 配置: &缠论配置) { - for (i, (key, rsv, k平滑, d平滑)) in 配置._解析KDJ参数列表().into_iter().enumerate() - { - let val = if let Some(prev_val) = prev.and_then(|p| p.获取(&key)) { + for (key, _fm, rsv, k平滑, d平滑, 超买, 超卖) in 配置.KDJ_参数列表.iter() { + let val = if let Some(prev_val) = prev.and_then(|p| p.获取(key)) { if let 指标值::KDJ(prev_kdj) = prev_val { 指标值::KDJ(随机指标::增量计算( prev_kdj, @@ -170,24 +162,19 @@ impl 指标计算器 { 当前K线.低, 当前K线.收盘价, 当前K线.时间戳, - rsv, - k平滑, - d平滑, - 配置.随机指标_超买阈值, - 配置.随机指标_超卖阈值, + *rsv, + *k平滑, + *d平滑, + *超买, + *超卖, )) }; - 当前K线.指标.write().设置(&key, val.clone()); - if i == 0 { - 当前K线.指标.write().设置("kdj", val); - } + 当前K线.指标.write().设置(key, val.clone()); } } fn _计算BOLL组(当前K线: &K线, prev: Option<&指标容器>, 配置: &缠论配置) { - let 计算方式 = &配置.指标计算方式; - for (i, (key, 周期, 标准差倍数)) in 配置._解析BOLL参数列表().into_iter().enumerate() - { + for (key, 计算方式, 周期, 标准差倍数) in 配置.BOLL_参数列表.iter() { let val = if let Some(prev_val) = prev.and_then(|p| p.获取(&key)) { if let 指标值::BOLL(prev_boll) = prev_val { 指标值::BOLL(布林带::增量计算( @@ -214,22 +201,37 @@ impl 指标计算器 { 当前K线.收盘价, 计算方式, ), - 周期 as usize, - 标准差倍数, + *周期 as usize, + *标准差倍数, )) }; 当前K线.指标.write().设置(&key, val.clone()); - if i == 0 { - 当前K线.指标.write().设置("boll", val); - } } } fn _更新均线(当前K线: &K线, 现有序列: &[Arc], 配置: &缠论配置) { - if 配置.均线_类型列表.is_empty() || 配置.均线_周期列表.is_empty() { + if 配置.均线参数列表.is_empty() { return; } - let 计算方式 = &配置.指标计算方式; + for (key, 计算方式, ma_type, period) in &配置.均线参数列表 { + let 值 = match ma_type.as_str() { + "SMA" => Self::_增量SMA(当前K线, 现有序列, 计算方式, *period, key), + "EMA" => Self::_增量EMA(当前K线, 现有序列, 计算方式, *period, key), + _ => continue, + }; + if let Some(均线_map) = 当前K线.指标.write().均线_mut() { + 均线_map.insert(key.clone(), 值); + } + } + } + + fn _增量SMA( + 当前K线: &K线, + 现有序列: &[Arc], + 计算方式: &str, + period: i64, + prev_key: &str, + ) -> f64 { let 当前价 = super::K线取值( 当前K线.开盘价, 当前K线.高, @@ -237,46 +239,20 @@ impl 指标计算器 { 当前K线.收盘价, 计算方式, ); - for ma_type in &配置.均线_类型列表 { - for period in &配置.均线_周期列表 { - let key = format!("{}_{}", ma_type, period); - let 值 = match ma_type.as_str() { - "SMA" => Self::_增量SMA(现有序列, 当前价, *period, 计算方式, &key), - "EMA" => Self::_增量EMA(现有序列, 当前价, *period, 计算方式, &key), - _ => continue, - }; - if let Some(均线_map) = 当前K线.指标.write().均线_mut() { - 均线_map.insert(key, 值); - } - } - } - } - - /// 增量 SMA: 现有序列 (不含当前K线) + 当前价 - fn _增量SMA( - 现有序列: &[Arc], - 当前价: f64, - period: i64, - 计算方式: &str, - prev_key: &str, - ) -> f64 { let existing_len = 现有序列.len(); let p = period as usize; - // 现有序列 + 当前 = total_len - let total_len = existing_len + 1; - if total_len <= p { - let mut sum: f64 = 现有序列[existing_len.saturating_sub(p.saturating_sub(1))..] + if existing_len + 1 <= p { + let sum: f64 = 现有序列 .iter() .map(|k| super::K线取值(k.开盘价, k.高, k.低, k.收盘价, 计算方式)) - .sum(); - sum += 当前价; - return sum / (total_len as f64).max(1.0); + .sum::() + + 当前价; + return sum / ((existing_len + 1) as f64).max(1.0); } - // 尝试从前一根K线获取缓存的SMA - if let Some(prev) = 现有序列.last().and_then(|k| { - let guard = k.指标.read(); - guard.均线().and_then(|m| m.get(prev_key)).copied() - }) { + if let Some(prev_sma) = 现有序列 + .last() + .and_then(|k| k.指标.read().均线().and_then(|m| m.get(prev_key)).copied()) + { let oldest = super::K线取值( 现有序列[existing_len - p].开盘价, 现有序列[existing_len - p].高, @@ -284,29 +260,33 @@ impl 指标计算器 { 现有序列[existing_len - p].收盘价, 计算方式, ); - return prev + (当前价 - oldest) / period as f64; + return prev_sma + (当前价 - oldest) / period as f64; } - // 回退:完整计算 - let mut sum: f64 = 现有序列[existing_len.saturating_sub(p.saturating_sub(1))..] + let sum: f64 = 现有序列[existing_len.saturating_sub(p.saturating_sub(1))..] .iter() .map(|k| super::K线取值(k.开盘价, k.高, k.低, k.收盘价, 计算方式)) - .sum(); - sum += 当前价; - sum / (total_len as f64).min(p as f64) + .sum::() + + 当前价; + sum / ((existing_len + 1) as f64).min(p as f64) } - /// 增量 EMA: 现有序列 (不含当前K线) + 当前价 fn _增量EMA( + 当前K线: &K线, 现有序列: &[Arc], - 当前价: f64, + 计算方式: &str, period: i64, - _计算方式: &str, prev_key: &str, ) -> f64 { - let 前值 = 现有序列.last().and_then(|k| { - let guard = k.指标.read(); - guard.均线().and_then(|m| m.get(prev_key)).copied() - }); + let 当前价 = super::K线取值( + 当前K线.开盘价, + 当前K线.高, + 当前K线.低, + 当前K线.收盘价, + 计算方式, + ); + let 前值 = 现有序列 + .last() + .and_then(|k| k.指标.read().均线().and_then(|m| m.get(prev_key)).copied()); match 前值 { None => 当前价, Some(prev) => { @@ -318,42 +298,40 @@ impl 指标计算器 { /// 运行中新增指标参数时,回填所有历史K线 fn _回填新指标(全序列: &[Arc], 配置: &缠论配置) { - // 作用域化首尾读锁:在回填写循环之前释放,避免读锁与写锁冲突 let (新MACD, 新RSI, 新KDJ, 新BOLL) = { let 首K_guard = 全序列[0].指标.read(); let 尾K_guard = 全序列[全序列.len() - 1].指标.read(); - let 新MACD: Vec<_> = 配置 - ._解析MACD参数列表() - .into_iter() - .filter(|(key, _, _, _)| 尾K_guard.包含(key) && !首K_guard.包含(key)) + .MACD_参数列表 + .iter() + .filter(|(key, ..)| 尾K_guard.包含(key) && !首K_guard.包含(key)) + .cloned() .collect(); let 新RSI: Vec<_> = 配置 - ._解析RSI周期列表() - .into_iter() - .filter(|(key, _)| 尾K_guard.包含(key) && !首K_guard.包含(key)) + .RSI_周期列表 + .iter() + .filter(|(key, ..)| 尾K_guard.包含(key) && !首K_guard.包含(key)) + .cloned() .collect(); let 新KDJ: Vec<_> = 配置 - ._解析KDJ参数列表() - .into_iter() - .filter(|(key, _, _, _)| 尾K_guard.包含(key) && !首K_guard.包含(key)) + .KDJ_参数列表 + .iter() + .filter(|(key, ..)| 尾K_guard.包含(key) && !首K_guard.包含(key)) + .cloned() .collect(); let 新BOLL: Vec<_> = 配置 - ._解析BOLL参数列表() - .into_iter() - .filter(|(key, _, _)| 尾K_guard.包含(key) && !首K_guard.包含(key)) + .BOLL_参数列表 + .iter() + .filter(|(key, ..)| 尾K_guard.包含(key) && !首K_guard.包含(key)) + .cloned() .collect(); - (新MACD, 新RSI, 新KDJ, 新BOLL) - }; // 首K_guard, 尾K_guard dropped here + }; if 新MACD.is_empty() && 新RSI.is_empty() && 新KDJ.is_empty() && 新BOLL.is_empty() { return; } - let 计算方式 = &配置.指标计算方式; - - // 从第一根K线开始逐根回填,每次只持有一根prev读锁 for i in 0..全序列.len() { let k线 = &全序列[i]; let prev_guard = if i > 0 { @@ -362,112 +340,62 @@ impl 指标计算器 { None }; - for (key, 快, 慢, 信号) in &新MACD { - let val = if let Some(ref prev) = prev_guard { - if let Some(指标值::MACD(prev_macd)) = prev.获取(key) { - 指标值::MACD(平滑异同移动平均线::增量计算_K线( - prev_macd, - k线, - 计算方式, - )) - } else { - 指标值::MACD(平滑异同移动平均线::首次计算_K线( - k线, - 计算方式, - *快, - *慢, - *信号, - )) - } - } else { - 指标值::MACD(平滑异同移动平均线::首次计算_K线( + for (key, 计算方式, 快, 慢, 信号) in &新MACD { + let val = match prev_guard.as_ref().and_then(|p| p.获取(key)) { + Some(指标值::MACD(prev_macd)) => 指标值::MACD( + 平滑异同移动平均线::增量计算_K线(prev_macd, k线, 计算方式), + ), + _ => 指标值::MACD(平滑异同移动平均线::首次计算_K线( k线, 计算方式, *快, *慢, *信号, - )) + )), }; k线.指标.write().设置(key, val); } - for (key, 周期) in &新RSI { - let val = if let Some(ref prev) = prev_guard { - if let Some(指标值::RSI(prev_rsi)) = prev.获取(key) { - 指标值::RSI(相对强弱指数::增量计算_K线( - prev_rsi, - k线, - 计算方式, - )) - } else { - 指标值::RSI(相对强弱指数::首次计算_K线( - k线, - 计算方式, - *周期, - 配置.相对强弱指数_超买阈值, - 配置.相对强弱指数_超卖阈值, - Some(配置.相对强弱指数_移动平均线周期), - )) - } - } else { - 指标值::RSI(相对强弱指数::首次计算_K线( + for (key, 计算方式, 周期, ma周期, 超买, 超卖) in &新RSI { + let val = match prev_guard.as_ref().and_then(|p| p.获取(key)) { + Some(指标值::RSI(prev_rsi)) => 指标值::RSI( + 相对强弱指数::增量计算_K线(prev_rsi, k线, 计算方式), + ), + _ => 指标值::RSI(相对强弱指数::首次计算_K线( k线, 计算方式, *周期, - 配置.相对强弱指数_超买阈值, - 配置.相对强弱指数_超卖阈值, - Some(配置.相对强弱指数_移动平均线周期), - )) + *超买, + *超卖, + Some(*ma周期), + )), }; k线.指标.write().设置(key, val); } - for (key, rsv, k平滑, d平滑) in &新KDJ { - let val = if let Some(ref prev) = prev_guard { - if let Some(指标值::KDJ(prev_kdj)) = prev.获取(key) { + for (key, _fm, rsv, k平滑, d平滑, 超买, 超卖) in &新KDJ { + let val = match prev_guard.as_ref().and_then(|p| p.获取(key)) { + Some(指标值::KDJ(prev_kdj)) => { 指标值::KDJ(随机指标::增量计算_K线(prev_kdj, k线)) - } else { - 指标值::KDJ(随机指标::首次计算_K线( - k线, - *rsv, - *k平滑, - *d平滑, - 配置.随机指标_超买阈值, - 配置.随机指标_超卖阈值, - )) } - } else { - 指标值::KDJ(随机指标::首次计算_K线( - k线, - *rsv, - *k平滑, - *d平滑, - 配置.随机指标_超买阈值, - 配置.随机指标_超卖阈值, - )) + _ => 指标值::KDJ(随机指标::首次计算_K线( + k线, *rsv, *k平滑, *d平滑, *超买, *超卖, + )), }; k线.指标.write().设置(key, val); } - for (key, 周期, 标准差倍数) in &新BOLL { - let val = if let Some(ref prev) = prev_guard { - if let Some(指标值::BOLL(prev_boll)) = prev.获取(key) { + for (key, 计算方式, 周期, 标准差倍数) in &新BOLL { + let val = match prev_guard.as_ref().and_then(|p| p.获取(key)) { + Some(指标值::BOLL(prev_boll)) => { 指标值::BOLL(布林带::增量计算_K线(prev_boll, k线, 计算方式)) - } else { - 指标值::BOLL(布林带::首次计算_K线( - k线, - 计算方式, - *周期 as usize, - *标准差倍数, - )) } - } else { - 指标值::BOLL(布林带::首次计算_K线( + _ => 指标值::BOLL(布林带::首次计算_K线( k线, 计算方式, *周期 as usize, *标准差倍数, - )) + )), }; k线.指标.write().设置(key, val); }