From bd4fceab02e65bbfd297b03d41ee91176b7542eb Mon Sep 17 00:00:00 2001 From: YuWuKunCheng Date: Sat, 30 May 2026 15:35:48 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=A8=E9=87=8F=E6=94=AF=E6=8C=81=20?= =?UTF-8?q?=E8=A7=82=E5=AF=9F=E8=80=85=E5=9C=A8python=E7=AB=AF=E7=9A=84?= =?UTF-8?q?=E9=87=8D=E5=86=99=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chan.py | 10 +++---- chanlun-py/Cargo.toml | 2 +- chanlun-py/chanlun/chan.py | 8 +++--- chanlun-py/pyproject.toml | 2 +- chanlun-py/src/business_py.rs | 31 ++++++++++++++++---- chanlun-py/src/kline_py.rs | 53 +++++++++++++++++++---------------- 6 files changed, 65 insertions(+), 41 deletions(-) diff --git a/chan.py b/chan.py index 2cef756..3075d21 100644 --- a/chan.py +++ b/chan.py @@ -5243,12 +5243,12 @@ class 观察者: buffer = f.read() size = struct.calcsize(">6d") for i in range(len(buffer) // size): - 时间戳, 开盘价, 最高价, 最低价, 收盘价, 成交量 = struct.unpack(">6d", buffer[i * size: i * size + size]) + 时间戳, 开盘价, 最高价, 最低价, 收盘价, 成交量 = struct.unpack(">6d", buffer[i * size : i * size + size]) self.投喂原始数据(转化为时间戳(int(时间戳)), 开盘价, 最高价, 最低价, 收盘价, 成交量) @classmethod - def 读取数据文件(cls,观察员:"观察者", 文件路径: str, 配置=缠论配置()) -> Self: - """ 加载数据文件 + def 读取数据文件(cls, 观察员: "观察者", 文件路径: str, 配置=缠论配置()) -> Self: + """加载数据文件 :param 观察员: 观察者 :param 文件路径: 数据文件路径 格式如: btcusd-300-1631772074-1632222374.nb :param 配置: 缠论配置 @@ -5513,7 +5513,7 @@ class 立体分析器: print(f"多级别数据拆分保存完成,目录:{保存路径.resolve()}") -def 测试_读取数据(观察员: 观察者, 配置: 缠论配置)-> Callable[[], "观察者"]: +def 测试_读取数据(观察员: 观察者, 配置: 缠论配置) -> Callable[[], "观察者"]: """测试_读取数据 :param 观察员: 观察者 :param 配置: 缠论配置 @@ -5563,5 +5563,5 @@ if __name__ == "__main__": 当前配置 = 缠论配置.不推送() 当前配置.加载文件路径 = str(Path(__file__).parent / "btcusd-300-1761327300-1776327900.nb") 观察员 = 观察者("", 0, 当前配置) - 测试_读取数据(观察员,当前配置)()# .测试_保存数据() + 测试_读取数据(观察员, 当前配置)() # .测试_保存数据() # 测试_周期合成(当前配置)().测试_保存数据() diff --git a/chanlun-py/Cargo.toml b/chanlun-py/Cargo.toml index 5edccb8..ecfe8fd 100644 --- a/chanlun-py/Cargo.toml +++ b/chanlun-py/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chanlun-py" -version = "26.5.102" +version = "26.5.103" edition = "2021" description = "缠论技术分析库 — Rust 高性能 Python 绑定" authors = ["YuYuKunKun"] diff --git a/chanlun-py/chanlun/chan.py b/chanlun-py/chanlun/chan.py index b75e6ec..2798cc9 100644 --- a/chanlun-py/chanlun/chan.py +++ b/chanlun-py/chanlun/chan.py @@ -5243,12 +5243,12 @@ class 观察者: buffer = f.read() size = struct.calcsize(">6d") for i in range(len(buffer) // size): - 时间戳, 开盘价, 最高价, 最低价, 收盘价, 成交量 = struct.unpack(">6d", buffer[i * size: i * size + size]) + 时间戳, 开盘价, 最高价, 最低价, 收盘价, 成交量 = struct.unpack(">6d", buffer[i * size : i * size + size]) self.投喂原始数据(转化为时间戳(int(时间戳)), 开盘价, 最高价, 最低价, 收盘价, 成交量) @classmethod - def 读取数据文件(cls,观察员:"观察者", 文件路径: str, 配置=缠论配置()) -> Self: - """ 加载数据文件 + def 读取数据文件(cls, 观察员: "观察者", 文件路径: str, 配置=缠论配置()) -> Self: + """加载数据文件 :param 观察员: 观察者 :param 文件路径: 数据文件路径 格式如: btcusd-300-1631772074-1632222374.nb :param 配置: 缠论配置 @@ -5513,7 +5513,7 @@ class 立体分析器: print(f"多级别数据拆分保存完成,目录:{保存路径.resolve()}") -def 测试_读取数据(观察员: 观察者, 配置: 缠论配置)-> Callable[()]: +def 测试_读取数据(观察员: 观察者, 配置: 缠论配置) -> Callable[()]: """测试_读取数据 :param 观察员: 观察者 :param 配置: 缠论配置 diff --git a/chanlun-py/pyproject.toml b/chanlun-py/pyproject.toml index 1650747..09bf3d7 100644 --- a/chanlun-py/pyproject.toml +++ b/chanlun-py/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "chanlun" -version = "2605.102" +version = "2605.103" description = "缠论技术分析库 — Rust 高性能实现" readme = { file = "README.md", content-type = "text/markdown" } license = { file = "LICENSE", content-type = "text/plain" } diff --git a/chanlun-py/src/business_py.rs b/chanlun-py/src/business_py.rs index 1ae7907..d7b4012 100644 --- a/chanlun-py/src/business_py.rs +++ b/chanlun-py/src/business_py.rs @@ -560,18 +560,37 @@ impl 观察者Py { self.obs_mut().增加原始K线((*普K.borrow().inner).clone()); } - /// 投喂原始数据 — 便捷入口,直接从 OHLCV 创建 K线 并投喂 + /// 投喂原始数据 — 便捷入口,直接从 OHLCV 创建 K线 并通过 Python 分发 增加原始K线, + /// 确保子类重写的 增加原始K线 被正确调用。 fn 投喂原始数据( - &mut self, 时间戳: i64, 开: f64, 高: f64, 低: f64, 收: f64, 量: f64 - ) { - self.obs_mut().投喂原始数据(时间戳, 开, 高, 低, 收, 量); + slf: &Bound<'_, Self>, + 时间戳: i64, + 开: f64, + 高: f64, + 低: f64, + 收: f64, + 量: f64, + ) -> PyResult<()> { + let (符号, 周期) = { + let me = slf.borrow(); + let obs = me.obs(); + (obs.符号.clone(), obs.周期) + }; + let kline = K线Py { + inner: Arc::new(chanlun::kline::bar::K线::创建普K( + &符号, 时间戳, 开, 高, 低, 收, 量, 0, 周期, + )), + }; + let kline_py = Py::new(slf.py(), kline)?; + slf.call_method1("增加原始K线", (kline_py,))?; + Ok(()) } /// 加载本地数据 — 从 .nb 文件加载K线数据(先重置,再通过 Python dispatch 逐根投喂, /// 确保子类重写的 增加原始K线 被正确调用)。 fn 加载本地数据(slf: &Bound<'_, Self>, 文件路径: &str) -> PyResult<()> { - // 重置基础序列 - slf.borrow_mut().obs_mut().重置基础序列(); + // 重置基础序列(通过 Python 分发,支持子类重写) + slf.call_method1("重置基础序列", ())?; // 读取文件,通过 Python dispatch 逐根投喂(支持子类重写 增加原始K线) let data = std::fs::read(文件路径) diff --git a/chanlun-py/src/kline_py.rs b/chanlun-py/src/kline_py.rs index 6849404..0f8faff 100644 --- a/chanlun-py/src/kline_py.rs +++ b/chanlun-py/src/kline_py.rs @@ -347,15 +347,11 @@ impl K线Py { #[pyclass(name = "缠论K线", module = "chanlun._chanlun", from_py_object)] pub struct 缠论K线Py { pub(crate) inner: std::sync::Arc, - bsp_set: std::sync::RwLock>>, } impl 缠论K线Py { pub(crate) fn from_rc(inner: std::sync::Arc) -> Self { - Self { - inner, - bsp_set: std::sync::RwLock::new(None), - } + Self { inner } } } @@ -366,6 +362,9 @@ thread_local! { static BAR_IDENTITY: RwLock>> = RwLock::new(HashMap::new()); static KLINE_IDENTITY: RwLock>> = RwLock::new(HashMap::new()); + + /// 买卖点信息缓存 — 按 Arc 指针全局共享,确保所有 wrapper 看到同一 PySet + static BSP_CACHE: RwLock>> = RwLock::new(HashMap::new()); } /// 将 Rc 转为 Py,确保同一 Rc 地址总是返回同一 Python 对象 @@ -408,7 +407,6 @@ impl Clone for 缠论K线Py { fn clone(&self) -> Self { Self { inner: std::sync::Arc::clone(&self.inner), - bsp_set: std::sync::RwLock::new(None), } } } @@ -530,16 +528,23 @@ impl 缠论K线Py { #[getter] /// 创建当前缠K的浅拷贝副本 fn 镜像(&self, py: Python<'_>) -> Self { - let mut mirror = Self { + let mirror = Self { inner: std::sync::Arc::new(self.inner.镜像()), - bsp_set: std::sync::RwLock::new(None), }; - if let Some(ref src_set) = *self.bsp_set.read().unwrap() { + // 复制买卖点信息到镜像 + let src_key = Arc::as_ptr(&self.inner) as usize; + let dst_key = Arc::as_ptr(&mirror.inner) as usize; + let cached_src = + BSP_CACHE.with(|c| c.read().unwrap().get(&src_key).map(|p| p.clone_ref(py))); + if let Some(cached_src) = cached_src { if let Ok(new_set) = pyo3::types::PySet::empty(py) { - for item in src_set.bind(py).iter() { + for item in cached_src.bind(py).iter() { let _ = new_set.add(item); } - mirror.bsp_set = std::sync::RwLock::new(Some(new_set.into())); + let py_set: Py = new_set.into(); + BSP_CACHE.with(|c| { + c.write().unwrap().insert(dst_key, py_set); + }); } } mirror @@ -565,20 +570,20 @@ impl 缠论K线Py { #[getter] fn 买卖点信息(&self, py: Python<'_>) -> PyResult> { - if self.bsp_set.read().unwrap().is_none() { - let set = pyo3::types::PySet::empty(py)?; - for s in self.inner.买卖点信息.read().unwrap().iter() { - set.add(s.clone())?; - } - *self.bsp_set.write().unwrap() = Some(set.into()); + let key = Arc::as_ptr(&self.inner) as usize; + // 检查全局缓存 + let cached = BSP_CACHE.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py))); + if let Some(set) = cached { + return Ok(set.into_any()); } - Ok(self - .bsp_set - .read() - .unwrap() - .as_ref() - .unwrap() - .clone_ref(py) + // 创建新的 PySet 并存入全局缓存 + let set = pyo3::types::PySet::empty(py)?; + BSP_CACHE.with(|c| { + c.write().unwrap().insert(key, set.into()); + }); + // 重新读取并返回(无法从 insert 获取 Py 引用,需要重新读) + Ok(BSP_CACHE + .with(|c| c.read().unwrap().get(&key).unwrap().clone_ref(py)) .into_any()) }