1. Pickle 支持修复(__reduce__)

- 修复了 相对方向Py.__reduce__ 中 str() 返回 相对方向.向上 导致 getattr 失败的问题(拆分取 . 后变体名)
  - 重新编译后 __module__ 正确返回 chanlun._chanlun
  - 所有 4 个类型(相对方向、买卖点类型、分型结构、缺口)pickle 往返测试通过

  2. from_py_object 消除 163 个弃用警告

  - 给 14 个 #[derive(Clone)] 的 pyclass 添加了 from_py_object
  - #[pyclass] 中的正确写法:#[pyclass(name = "X", module = "chanlun._chanlun", from_py_object)]
  - cargo clean 重新编译后零 from_py_object 警告

  3. #[classattr] + __members__

  - 为 3 个枚举类型(相对方向、买卖点类型、分型结构)添加了 __members__ 类属性
  - __members__ 是 dict[str, 实例],行为与 Python Enum.__members__ 一致
  - 通过 pickle 反序列化的值也在 __members__.values() 中

  4. __richcmp__

  - 为 3 个枚举类型 + 缺口实现了 __richcmp__,替换手写 __eq__
  - 相对方向/分型结构:支持全部 6 种比较(基于判别值排序)
  - 买卖点类型:支持 Eq/Ne + 与字符串比较
  - 缺口:支持全部 6 种比较(按 (高, 低) 元组排序)

  5. 补充文档
This commit is contained in:
YuWuKunCheng
2026-05-28 14:03:53 +08:00
parent 52823e6c75
commit ae57090d7f
15 changed files with 5259 additions and 50 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "chanlun-py"
version = "26.5.46"
version = "26.5.67"
edition = "2021"
description = "缠论技术分析库 — Rust 高性能 Python 绑定"
authors = ["YuYuKunKun"]
@@ -13,6 +13,6 @@ name = "chanlun"
[dependencies]
chanlun = "26.5.2" # { path = "../chanlun" }
pyo3 = { version = "0.28", features = ["extension-module"] }
pyo3 = { version = "0.28", features = ["extension-module", "experimental-inspect"] }
serde_json = "1"
chrono = "0.4"
+68
View File
@@ -0,0 +1,68 @@
/// build.rs — 编译时版本一致性检查
///
/// 验证 Cargo.toml 和 pyproject.toml 的版本号是否一致。
///
/// Cargo.toml: version = "YY.MM.patch" (e.g., "26.5.57")
/// pyproject.toml: version = "YYMM.patch" (e.g., "2605.57")
/// 规则: YY = major, MM = minor, patch = patch
fn main() {
let cargo_manifest_dir =
std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
let cargo_path = std::path::PathBuf::from(&cargo_manifest_dir);
let cargo_version = read_version(&cargo_path.join("Cargo.toml"), "[package]");
let cargo_parts: Vec<&str> = cargo_version.split('.').collect();
assert_eq!(
cargo_parts.len(),
3,
"Cargo.toml version '{}' is not in YY.MM.patch format",
cargo_version
);
let pyproject_version = read_version(&cargo_path.join("pyproject.toml"), "[project]");
let py_parts: Vec<&str> = pyproject_version.split('.').collect();
assert_eq!(
py_parts.len(),
2,
"pyproject.toml version '{}' is not in YYMM.patch format",
pyproject_version
);
let expected_yymm = format!("{}{:0>2}", cargo_parts[0], cargo_parts[1]);
assert_eq!(
py_parts[0], expected_yymm,
"pyproject.toml version prefix '{}' != expected '{}' (from Cargo {})",
py_parts[0], expected_yymm, cargo_version
);
assert_eq!(
py_parts[1], cargo_parts[2],
"pyproject.toml patch '{}' != Cargo.toml patch '{}'",
py_parts[1], cargo_parts[2]
);
println!("cargo:rerun-if-changed=pyproject.toml");
println!("cargo:rerun-if-changed=Cargo.toml");
}
fn read_version(path: &std::path::Path, section: &str) -> String {
let content =
std::fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read {:?}: {}", path, e));
let mut in_section = section.is_empty();
for line in content.lines() {
let trimmed = line.trim();
if trimmed.starts_with('[') {
in_section = trimmed == section;
continue;
}
if !in_section {
continue;
}
if trimmed.starts_with("version") {
if let Some(v) = trimmed.split('=').nth(1) {
return v.trim().trim_matches('"').trim().to_string();
}
}
}
panic!("Cannot parse version from {:?}", path);
}
File diff suppressed because it is too large Load Diff
+30
View File
@@ -1,3 +1,33 @@
"""缠论技术分析库 — Rust 高性能实现"""
__all__ = [
"K线",
"K线合成器",
"中枢",
"买卖点",
"买卖点类型",
"分型",
"分型结构",
"基础买卖点",
"平滑异同移动平均线",
"指标",
"测试_读取数据",
"特征分型",
"相对强弱指数",
"相对方向",
"立体分析器",
"",
"线段",
"线段特征",
"缠论K线",
"缠论配置",
"缺口",
"背驰分析",
"虚线",
"观察者",
"转化为时间戳",
"转化为时间戳_数字",
"随机指标",
]
from ._chanlun import *
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "maturin"
[project]
name = "chanlun"
version = "2605.46"
version = "2605.67"
description = "缠论技术分析库 — Rust 高性能实现"
readme = { file = "README.md", content-type = "text/markdown" }
license = { file = "LICENSE", content-type = "text/plain" }
+95 -5
View File
@@ -23,7 +23,7 @@
*/
use pyo3::prelude::*;
use pyo3::types::PyType;
use pyo3::types::{PyDict, PyType};
use std::rc::Rc;
use crate::business_py::Py;
@@ -44,13 +44,14 @@ use crate::types_py::相对方向Py;
/// MACD盘整背驰(虚线段, 线段序列, 中枢序列, 配置) -> bool
/// MACD柱子面积背驰(虚线段, 线段序列, 中枢序列, 配置) -> bool
/// MACD柱子高度背驰(虚线段, 线段序列, 中枢序列, 配置) -> bool
#[pyclass(name = "背驰分析")]
#[pyclass(name = "背驰分析", module = "chanlun._chanlun")]
pub struct Py;
#[pymethods]
impl Py {
#[classmethod]
#[pyo3(signature = (进入段, 离开段, K线序列, 方式 = ""))]
/// MACD柱状线面积背驰
fn MACD背驰(
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -72,6 +73,7 @@ impl 背驰分析Py {
}
#[classmethod]
/// 价格斜率背驰
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -84,6 +86,7 @@ impl 背驰分析Py {
}
#[classmethod]
/// 价格测度背驰(欧氏距离)
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -96,6 +99,7 @@ impl 背驰分析Py {
}
#[classmethod]
/// 判断是否满足全部三种背驰条件(MACD + 测度 + 斜率)
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -115,6 +119,7 @@ impl 背驰分析Py {
}
#[classmethod]
/// 判断是否满足任一背驰条件
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -134,6 +139,7 @@ impl 背驰分析Py {
}
#[classmethod]
/// 根据配置选择对应的背驰检测组合
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -156,6 +162,7 @@ impl 背驰分析Py {
}
#[classmethod]
/// 三个背驰条件中至少两个满足即视为背驰
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -175,6 +182,7 @@ impl 背驰分析Py {
}
#[classmethod]
/// 根据模式字符串选择背驰检测策略
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -212,12 +220,13 @@ impl 背驰分析Py {
/// — 从分型序列中划分出所有笔
/// 弱化(笔序列) — 根据配置对笔序列执行弱化处理
/// 分析前检查(分型序列, 配置) -> bool — 检查是否可以启动笔分析
#[pyclass(name = "")]
#[pyclass(name = "", module = "chanlun._chanlun")]
pub struct Py;
#[pymethods]
impl Py {
#[classmethod]
/// 获取笔内有效缠K数量(考虑笔弱化等配置)
fn K数量(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<crate::kline_py::K线Py>>,
@@ -240,6 +249,7 @@ impl 笔Py {
}
#[classmethod]
/// 次高
fn (
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<crate::kline_py::K线Py>>,
@@ -255,6 +265,7 @@ impl 笔Py {
}
#[classmethod]
/// 次低
fn (
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<crate::kline_py::K线Py>>,
@@ -270,6 +281,7 @@ impl 笔Py {
}
#[classmethod]
/// 实际高点
fn (
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<crate::kline_py::K线Py>>,
@@ -285,6 +297,7 @@ impl 笔Py {
}
#[classmethod]
/// 实际低点
fn (
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<crate::kline_py::K线Py>>,
@@ -300,6 +313,7 @@ impl 笔Py {
}
#[classmethod]
/// 相对关系
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -314,6 +328,7 @@ impl 笔Py {
}
#[classmethod]
/// 以文会友
fn (
_cls: &Bound<'_, PyType>,
: Vec<Py<线Py>>,
@@ -329,6 +344,7 @@ impl 笔Py {
}
#[classmethod]
/// 以武会友
fn (
_cls: &Bound<'_, PyType>,
: Vec<Py<线Py>>,
@@ -345,6 +361,7 @@ impl 笔Py {
#[classmethod]
#[pyo3(signature = (笔序列, 缠K, 偏移 = 1))]
/// 根据缠K找笔
fn K找笔(
_cls: &Bound<'_, PyType>,
: Vec<Py<线Py>>,
@@ -362,6 +379,7 @@ impl 笔Py {
#[classmethod]
#[pyo3(signature = (当前分型, 分型序列, 笔序列, 缠K序列, 普K序列, 递归层次, 配置))]
/// 笔划分核心递归算法
fn (
_cls: &Bound<'_, PyType>,
: Option<&Bound<'_, Py>>,
@@ -446,6 +464,7 @@ impl 笔Py {
}
#[classmethod]
/// 校验笔的有效性:高/低点是否为实际极值
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -457,6 +476,7 @@ impl 笔Py {
}
#[classmethod]
/// 获取笔内所有可能的停顿位置(用于背驰检测)
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -471,6 +491,7 @@ impl 笔Py {
}
#[classmethod]
/// 判断笔内是否发生过MACD趋向背驰
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -505,12 +526,13 @@ impl 笔Py {
/// 虚线段特征序列成立(虚线段, 虚线序列, 配置) -> bool
/// 移除包含后的特征序列(特征序列, 配置) -> list[线段特征]
/// 检查特征序列趋势(特征序列) -> bool
#[pyclass(name = "线段")]
#[pyclass(name = "线段", module = "chanlun._chanlun")]
pub struct 线Py;
#[pymethods]
impl 线Py {
#[classmethod]
/// 向线段中添加一笔
fn 线(
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -523,6 +545,7 @@ impl 线段Py {
}
#[classmethod]
/// 更新线段的终点分型(武)
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -539,6 +562,7 @@ impl 线段Py {
}
#[classmethod]
/// 武终
fn (_cls: &Bound<'_, PyType>, : &Bound<'_, 线Py>, : u32) -> PyResult<()> {
let mut ref_mut = .borrow_mut();
chanlun::algorithm::segment::线::(&mut ref_mut.inner, );
@@ -546,6 +570,7 @@ impl 线段Py {
}
#[classmethod]
/// 验证序列
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -562,6 +587,7 @@ impl 线段Py {
}
#[classmethod]
/// 序列重置
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -578,6 +604,7 @@ impl 线段Py {
}
#[classmethod]
/// 连续三笔且重叠
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -595,11 +622,13 @@ impl 线段Py {
}
#[classmethod]
/// 判断线段的四象属性
fn (_cls: &Bound<'_, PyType>, : &Bound<'_, 线Py>) -> String {
chanlun::algorithm::segment::线::(&.borrow().inner)
}
#[classmethod]
/// 获取线段特征序列第一二元素间的缺口
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -609,11 +638,13 @@ impl 线段Py {
}
#[classmethod]
/// 是否符合特征序列正常分型终结
fn (_cls: &Bound<'_, PyType>, : &Bound<'_, 线Py>) -> bool {
chanlun::algorithm::segment::线::(&.borrow().inner)
}
#[classmethod]
/// :param 段: 线段
fn (
_cls: &Bound<'_, PyType>, : &Bound<'_, 线Py>
) -> (bool, bool, bool) {
@@ -621,6 +652,7 @@ impl 线段Py {
}
#[classmethod]
/// 设置特征序列
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -651,6 +683,7 @@ impl 线段Py {
}
#[classmethod]
/// 刷新特征序列
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -664,12 +697,14 @@ impl 线段Py {
}
#[classmethod]
/// 查找贯穿伤
fn 穿(_cls: &Bound<'_, PyType>, : &Bound<'_, 线Py>) -> Option<线Py> {
chanlun::algorithm::segment::线::穿(&.borrow().inner)
.map(|inner| 线Py { inner })
}
#[classmethod]
/// 将线段基础序列分割为前/后/第三买卖/贯穿伤四部分
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -708,6 +743,7 @@ impl 线段Py {
}
#[classmethod]
/// 刷新线段的特征序列和内部中枢序列
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -721,6 +757,7 @@ impl 线段Py {
}
#[classmethod]
/// 获取内部中枢序列
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -744,6 +781,7 @@ impl 线段Py {
}
#[classmethod]
/// 内部方法:向线段序列添加新线段
fn _添加线段(
_cls: &Bound<'_, PyType>,
线: &Bound<'_, PyAny>,
@@ -764,6 +802,7 @@ impl 线段Py {
}
#[classmethod]
/// 内部方法:从线段序列弹出最后一个线段
fn _弹出线段(
_cls: &Bound<'_, PyType>,
线: &Bound<'_, PyAny>,
@@ -784,6 +823,7 @@ impl 线段Py {
}
#[classmethod]
/// 内部方法:处理缺口突破修正
fn _缺口突破(
_cls: &Bound<'_, PyType>,
线: Vec<Py<线Py>>,
@@ -804,6 +844,7 @@ impl 线段Py {
}
#[classmethod]
/// 内部方法:处理非缺口下穿刺修正
fn _非缺口下穿刺(
_cls: &Bound<'_, PyType>,
线: Vec<Py<线Py>>,
@@ -824,6 +865,7 @@ impl 线段Py {
}
#[classmethod]
/// 内部方法:处理缺口后紧急修正
fn _缺口后紧急修正(
_cls: &Bound<'_, PyType>,
线: Vec<Py<线Py>>,
@@ -844,6 +886,7 @@ impl 线段Py {
}
#[classmethod]
/// 内部方法:处理线段修正
fn _修正(
_cls: &Bound<'_, PyType>,
线: Vec<Py<线Py>>,
@@ -865,6 +908,7 @@ impl 线段Py {
#[classmethod]
#[pyo3(signature = (笔序列, 线段序列, 配置, 层级 = 0, 关系序列 = None))]
/// 线段划分核心递归算法
fn (
_cls: &Bound<'_, PyType>,
: Vec<Py<线Py>>,
@@ -901,6 +945,7 @@ impl 线段Py {
}
#[classmethod]
/// 内部方法:向扩展线段序列添加新线段
fn _添加扩展线段(
_cls: &Bound<'_, PyType>,
线: Vec<Py<线Py>>,
@@ -921,6 +966,7 @@ impl 线段Py {
}
#[classmethod]
/// 内部方法:从扩展线段序列弹出最后一个线段
fn _弹出扩展线段(
_cls: &Bound<'_, PyType>,
线: Vec<Py<线Py>>,
@@ -941,6 +987,7 @@ impl 线段Py {
}
#[classmethod]
/// 即同级别分析
fn (
_cls: &Bound<'_, PyType>,
线: Vec<Py<线Py>>,
@@ -962,6 +1009,7 @@ impl 线段Py {
}
#[classmethod]
/// 判断线段内部是否发生背驰(基于内部中枢和MACD)
fn 线(
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -976,6 +1024,7 @@ impl 线段Py {
}
#[classmethod]
/// 获取所有停顿位置
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -990,6 +1039,7 @@ impl 线段Py {
}
#[classmethod]
/// 判断线段内是否发生过背驰(遍历所有停顿位置)
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -1043,7 +1093,7 @@ impl 线段Py {
/// 向中枢序列尾部添加(中枢序列, 新中枢) — 维护中枢序列顺序
/// 从中枢序列尾部弹出(中枢序列, 配置) — 弹出最后一个中枢并更新相邻中枢
/// 分析(虚线序列, 中枢序列容器, 允许重叠?, 标识前缀?, ...) — 中枢分析主流程
#[pyclass(name = "中枢", unsendable)]
#[pyclass(name = "中枢", module = "chanlun._chanlun", unsendable, from_py_object)]
#[derive(Clone)]
pub struct Py {
pub(crate) inner: Rc<chanlun::algorithm::hub::>,
@@ -1106,17 +1156,20 @@ impl 中枢Py {
})
}
/// 向中枢添加新虚线(延伸),重置第三买卖线
fn 线(&mut self, 线: &Bound<'_, 线Py>) {
let inner = Rc::make_mut(&mut self.inner);
inner.线(Rc::clone(&线.borrow().inner));
}
#[getter]
/// :return: 图表标题
fn (&self) -> String {
self.inner.()
}
#[getter]
/// :return: 最后一条虚线
fn (&self) -> 线Py {
线Py {
inner: self.inner.(),
@@ -1124,6 +1177,7 @@ impl 中枢Py {
}
#[getter]
/// :return: 中枢方向(首条虚线的方向翻转)
fn (&self) -> Py {
Py {
inner: self.inner.(),
@@ -1131,26 +1185,31 @@ impl 中枢Py {
}
#[getter]
/// :return: 中枢上沿(前三段中虚线高点的最小值)
fn (&self) -> f64 {
self.inner.()
}
#[getter]
/// :return: 中枢下沿(前三段中虚线低点的最大值)
fn (&self) -> f64 {
self.inner.()
}
#[getter]
/// :return: 全区间最高价
fn (&self) -> f64 {
self.inner.()
}
#[getter]
/// :return: 全区间最低价
fn (&self) -> f64 {
self.inner.()
}
#[getter]
/// :return: 起点分型
fn (&self) -> Py {
Py {
inner: self.inner.(),
@@ -1158,17 +1217,20 @@ impl 中枢Py {
}
#[getter]
/// :return: 终点分型
fn (&self) -> Py {
Py {
inner: self.inner.(),
}
}
/// 设置第三类买卖点关联虚线
fn 线(&mut self, 线: &Bound<'_, 线Py>) {
let inner = Rc::make_mut(&mut self.inner);
inner.线(Rc::clone(&线.borrow().inner));
}
/// 获取中枢的完整虚线序列(基础序列+第三买卖线)
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for d in self.inner.() {
@@ -1177,11 +1239,13 @@ impl 中枢Py {
Ok(list.into())
}
/// 获取用于保存的数据文本
fn (&self) -> String {
self.inner.()
}
#[pyo3(signature = (序列, 中枢序列 = None))]
/// 校验当前中枢在给定序列中是否仍然合法
fn (
&mut self,
: Vec<Py<线Py>>,
@@ -1198,10 +1262,12 @@ impl 中枢Py {
}
#[pyo3(signature = (虚实 = ""))]
/// 判断中枢是否完整(是否有第三买卖点或内部中枢离开)
fn (&self, : &str) -> bool {
self.inner.()
}
/// 当基础序列>=9时,从中枢中提取扩展线段中枢
fn (
&self,
: Vec<Py<Self>>,
@@ -1217,10 +1283,28 @@ impl 中枢Py {
Ok(())
}
/// 获取中枢当前状态:中枢之中/中枢之上/中枢之下
fn (&self) -> String {
self.inner.().to_string()
}
/// pandas 兼容 — 返回关键标量字段构成的字典
#[getter]
fn __dict__(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
let dict = PyDict::new(py);
dict.set_item("序号", self.())?;
dict.set_item("标识", self.())?;
dict.set_item("级别", self.())?;
dict.set_item("图表标题", self.())?;
dict.set_item("方向", self.())?;
dict.set_item("", self.())?;
dict.set_item("", self.())?;
dict.set_item("高高", self.())?;
dict.set_item("低低", self.())?;
dict.set_item("当前状态", self.())?;
Ok(dict.into())
}
fn __str__(&self) -> String {
format!("{}", self.inner)
}
@@ -1243,6 +1327,7 @@ impl 中枢Py {
// ---- classmethods ----
#[classmethod]
/// 检查三条虚线是否构成中枢(连续且重叠)
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -1258,6 +1343,7 @@ impl 中枢Py {
#[classmethod]
#[pyo3(signature = (左, 中, 右, 级别, 标识 = ""))]
/// 从三条连续且重叠的虚线创建中枢
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>,
@@ -1278,6 +1364,7 @@ impl 中枢Py {
}
#[classmethod]
/// 从虚线序列中按起始方向查找第一个中枢
fn (
_cls: &Bound<'_, PyType>,
线: Vec<Py<线Py>>,
@@ -1298,6 +1385,7 @@ impl 中枢Py {
}
#[classmethod]
/// :param 中枢序列: 中枢列表
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, PyAny>,
@@ -1310,6 +1398,7 @@ impl 中枢Py {
}
#[classmethod]
/// :param 中枢序列: 中枢列表
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, PyAny>,
@@ -1327,6 +1416,7 @@ impl 中枢Py {
#[classmethod]
#[pyo3(signature = (虚线序列, 中枢序列, 跳过首部 = true, 标识 = "", 层级 = 0))]
/// 中枢识别核心递归算法
fn (
_cls: &Bound<'_, PyType>,
线: Vec<Py<线Py>>,
+57 -6
View File
@@ -23,7 +23,7 @@
*/
use pyo3::prelude::*;
use pyo3::types::PyType;
use pyo3::types::{PyDict, PyType};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
@@ -44,7 +44,12 @@ use crate::types_py::买卖点类型Py;
/// 有效性: bool — 买卖点是否仍有效
/// 与MACD柱子匹配: bool|None — 是否与MACD柱状图方向匹配
/// 与MACD柱子分型匹配: bool|None — 是否与MACD柱分型匹配
#[pyclass(name = "基础买卖点", unsendable)]
#[pyclass(
name = "基础买卖点",
module = "chanlun._chanlun",
unsendable,
from_py_object
)]
#[derive(Clone)]
pub struct Py {
pub(crate) inner: chanlun::business::bsp::,
@@ -102,6 +107,7 @@ impl 基础买卖点Py {
}
#[getter]
/// 当前K线
fn K线(&self) -> K线Py {
K线Py {
inner: self.inner.K线.clone(),
@@ -123,6 +129,7 @@ impl 基础买卖点Py {
}
#[getter]
/// 破位值
fn (&self) -> f64 {
self.inner.
}
@@ -135,30 +142,53 @@ impl 基础买卖点Py {
}
#[getter]
/// 偏移
fn (&self) -> i64 {
self.inner.()
}
#[getter]
/// 失效偏移
fn (&self) -> i64 {
self.inner.()
}
#[getter]
/// 有效性
fn (&self) -> bool {
self.inner.()
}
#[getter]
/// 与MACD柱子匹配
fn MACD柱子匹配(&self) -> bool {
self.inner.MACD柱子匹配()
}
#[getter]
/// 与MACD柱子分型匹配
fn MACD柱子分型匹配(&self) -> bool {
self.inner.MACD柱子分型匹配()
}
/// pandas 兼容 — 返回关键标量字段构成的字典
#[getter]
fn __dict__(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
let dict = PyDict::new(py);
dict.set_item("备注", self.())?;
dict.set_item("类型", self.())?;
dict.set_item("破位值", self.())?;
dict.set_item("偏移", self.())?;
dict.set_item("失效偏移", self.())?;
dict.set_item("有效性", self.())?;
dict.set_item("与MACD柱子匹配", self.MACD柱子匹配())?;
dict.set_item("与MACD柱子分型匹配", self.MACD柱子分型匹配())?;
if let Some(v) = self.() {
dict.set_item("结构", v)?;
}
Ok(dict.into())
}
fn __str__(&self) -> String {
format!("{}", self.inner)
}
@@ -176,12 +206,13 @@ impl 基础买卖点Py {
/// 一卖点(...) / 一买点(...) / 二卖点(...) / 二买点(...) / 三卖点(...) / 三买点(...)
/// 生成买卖点(特征, 序号, 级别, 分型, 当前缠K, 备注?) -> 买卖点
/// — 根据特征字符串自动路由到对应的一/二/三类买卖点构造函数
#[pyclass(name = "买卖点")]
#[pyclass(name = "买卖点", module = "chanlun._chanlun")]
pub struct Py;
#[pymethods]
impl Py {
#[classmethod]
/// :param 买卖点分型: 买卖点对应的分型
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, Py>,
@@ -202,6 +233,7 @@ impl 买卖点Py {
}
#[classmethod]
/// :param 买卖点分型: 买卖点对应的分型
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, Py>,
@@ -222,6 +254,7 @@ impl 买卖点Py {
}
#[classmethod]
/// :param 买卖点分型: 买卖点对应的分型
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, Py>,
@@ -242,6 +275,7 @@ impl 买卖点Py {
}
#[classmethod]
/// :param 买卖点分型: 买卖点对应的分型
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, Py>,
@@ -262,6 +296,7 @@ impl 买卖点Py {
}
#[classmethod]
/// :param 买卖点分型: 买卖点对应的分型
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, Py>,
@@ -282,6 +317,7 @@ impl 买卖点Py {
}
#[classmethod]
/// :param 买卖点分型: 买卖点对应的分型
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, Py>,
@@ -302,6 +338,7 @@ impl 买卖点Py {
}
#[classmethod]
/// :param 特征: 特征字符串
fn (
_cls: &Bound<'_, PyType>,
: &str,
@@ -353,7 +390,7 @@ impl 买卖点Py {
/// 调试方法:
/// 测试_保存数据(root?) — 将各层级序列保存到文件,用于与Python版对比
/// 读取数据文件(文件路径, 配置) -> 观察者 (classmethod) — 从 .nb 文件加载数据
#[pyclass(name = "观察者", subclass, unsendable)]
#[pyclass(name = "观察者", module = "chanlun._chanlun", subclass, unsendable)]
pub struct Py {
pub(crate) inner: Option<Rc<RefCell<chanlun::business::observer::>>>,
}
@@ -442,16 +479,19 @@ impl 观察者Py {
}
#[getter]
/// 观察员(自引用)
fn (slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
slf
}
#[getter]
/// :return: "{符号}:{周期}"
fn (&self) -> String {
self.obs().()
}
#[getter]
/// :return: 最后一根原始K线
fn K线(&self) -> Option<K线Py> {
self.obs().K线().map(|k| K线Py {
inner: Rc::clone(k),
@@ -459,6 +499,7 @@ impl 观察者Py {
}
#[getter]
/// :return: 最后一根缠论K线
fn K(&self) -> Option<K线Py> {
self.obs()
.K()
@@ -480,10 +521,12 @@ impl 观察者Py {
Py::from_rust_config(&self.obs().)
}
/// 清空所有分析序列,重置为初始状态
fn (&mut self) {
self.obs_mut().();
}
/// 核心入口 — 投喂一根原始K线,增量更新所有层级
fn K线(&mut self, K: &Bound<'_, K线Py>) {
self.obs_mut().K线((*K.borrow().inner).clone());
}
@@ -516,16 +559,19 @@ impl 观察者Py {
Ok(())
}
/// 静态重新分析(占位方法)
fn (&mut self) {
self.obs_mut().();
}
/// 拆分各序列数据,单独存文件,文件名为对应变量名
fn _保存数据(&self, root: Option<&str>) {
self.obs()._保存数据(root);
}
#[classmethod]
#[pyo3(signature = (文件路径, 配置 = None))]
/// :param 文件路径: 数据文件路径 格式如: btcusd-300-1631772074-1632222374.nb
fn (
cls: &Bound<'_, PyType>,
: &str,
@@ -760,7 +806,7 @@ impl 观察者Py {
/// 投喂(时间戳, 开盘价, 最高价, 最低价, 收盘价, 成交量) -> list[(周期, K线)]
/// — 快捷入口,免去构造K线对象
/// 获取当前K线(周期) -> K线|None — 获取指定周期的当前合成结果
#[pyclass(name = "K线合成器", unsendable)]
#[pyclass(name = "K线合成器", module = "chanlun._chanlun", unsendable)]
pub struct K线合成器Py {
pub(crate) inner: chanlun::business::synthesizer::K线合成器,
}
@@ -774,6 +820,7 @@ impl K线合成器Py {
}
}
/// 统一入口 — 投喂最小周期K线,自动合成大周期并分发给各周期观察者
fn K线(
&mut self,
K: &Bound<'_, K线Py>,
@@ -786,6 +833,7 @@ impl K线合成器Py {
.collect())
}
/// 投喂原始tick数据
fn (
&mut self,
: i64,
@@ -814,6 +862,7 @@ impl K线合成器Py {
.collect()
}
/// 获取指定周期当前正在合成的K线
fn K线(&self, : i64) -> Option<K线Py> {
self.inner.K线().map(|k| K线Py {
inner: Rc::new(k.clone()),
@@ -844,7 +893,7 @@ impl K线合成器Py {
/// 方法:
/// 投喂K线(普K) — 喂入最小周期K线,自动合成大周期并分发给各周期观察者
/// 测试_保存数据(root?) — 保存各周期的分析数据到文件
#[pyclass(name = "立体分析器", unsendable)]
#[pyclass(name = "立体分析器", module = "chanlun._chanlun", unsendable)]
pub struct Py {
pub(crate) inner: chanlun::business::multi_frame::,
}
@@ -884,6 +933,7 @@ impl 立体分析器Py {
})
}
/// 统一入口 — 投喂最小周期K线,自动合成大周期并分发给各周期观察者
fn K线(&mut self, K: &Bound<'_, K线Py>) {
self.inner.K线((*K.borrow().inner).clone());
}
@@ -894,6 +944,7 @@ impl 立体分析器Py {
.map(|rc| Py { inner: Some(rc) })
}
/// 拆分各序列数据,单独存文件,文件名为对应变量名
fn _保存数据(&self) {
self.inner._保存数据();
}
+6 -1
View File
@@ -91,7 +91,7 @@ use std::collections::HashMap;
/// 不推送() -> 缠论配置 (classmethod) — 创建关闭所有推送的配置副本
/// 按序号重组字典(默认配置, 原始字典) -> dict (classmethod) — 按默认配置的键序重排字典
/// 对比(other) -> dict — 返回与另一个配置的差异字段
#[pyclass(name = "缠论配置")]
#[pyclass(name = "缠论配置", module = "chanlun._chanlun")]
pub struct Py {
fields: HashMap<String, Py<PyAny>>,
}
@@ -203,6 +203,7 @@ impl 缠论配置Py {
}
#[classmethod]
/// :param data: 字典数据
fn from_dict(_cls: &Bound<'_, PyType>, data: &Bound<'_, PyDict>) -> PyResult<Self> {
let default_config = chanlun::config::::default();
let mut fields = config_to_field_dict(&default_config)?;
@@ -220,11 +221,13 @@ impl 缠论配置Py {
}
#[classmethod]
/// :param json_str: JSON字符串
fn from_json(_cls: &Bound<'_, PyType>, py: Python<'_>, json_str: &str) -> PyResult<Self> {
Self::from_json_str(py, json_str)
}
#[classmethod]
/// 创建不推送任何图表的静默配置(用于纯计算场景)
fn (_cls: &Bound<'_, PyType>) -> PyResult<Self> {
let config = chanlun::config::::default().();
let fields = config_to_field_dict(&config)?;
@@ -232,6 +235,7 @@ impl 缠论配置Py {
}
#[classmethod]
/// 将形如 "1_open", "1_close", "2_open", "name" 的字典重组为嵌套结构
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, PyAny>,
@@ -251,6 +255,7 @@ impl 缠论配置Py {
Ok(result.into())
}
/// 比较当前配置与另一个配置的差异
fn (
&self,
py: Python<'_>,
+21 -4
View File
@@ -43,7 +43,11 @@ use pyo3::types::PyType;
/// 增量计算(前一个MACD, 当前收盘价, 当前时间) -> 平滑异同移动平均线
/// — 基于前一根的 EMA 状态增量更新,用于流式计算
/// 增量计算_K线(前一个MACD, 当前K线, 计算方式) -> 平滑异同移动平均线
#[pyclass(name = "平滑异同移动平均线")]
#[pyclass(
name = "平滑异同移动平均线",
module = "chanlun._chanlun",
from_py_object
)]
#[derive(Clone)]
pub struct 线Py {
pub(crate) inner: chanlun::indicators::线,
@@ -124,6 +128,7 @@ impl 平滑异同移动平均线Py {
#[classmethod]
#[pyo3(signature = (初始收盘价, 初始时间, 快线周期 = None, 慢线周期 = None, 信号周期 = None))]
/// 首次计算MACD指标(没有历史数据时使用)
fn (
_cls: &Bound<'_, PyType>,
: f64,
@@ -144,6 +149,7 @@ impl 平滑异同移动平均线Py {
#[classmethod]
#[pyo3(signature = (k线, 计算方式, 快线周期 = None, 慢线周期 = None, 信号周期 = None))]
/// :param k线: 原始K线
fn _K线(
_cls: &Bound<'_, PyType>,
k线: &Bound<'_, PyAny>,
@@ -165,6 +171,7 @@ impl 平滑异同移动平均线Py {
}
#[classmethod]
/// 基于前一个MACD指标增量计算当前MACD指标
fn (
_cls: &Bound<'_, PyType>,
MACD: &Bound<'_, 线Py>,
@@ -180,6 +187,7 @@ impl 平滑异同移动平均线Py {
}
#[classmethod]
/// :param 前一个MACD: 前一个MACD指标对象
fn _K线(
_cls: &Bound<'_, PyType>,
MACD: &Bound<'_, 线Py>,
@@ -214,7 +222,7 @@ impl 平滑异同移动平均线Py {
/// 首次计算_K线(k线, 计算方式, ...)
/// 增量计算(前一个RSI, 当前收盘价, 当前时间)
/// 增量计算_K线(前一个RSI, 当前K线, 计算方式)
#[pyclass(name = "相对强弱指数")]
#[pyclass(name = "相对强弱指数", module = "chanlun._chanlun", from_py_object)]
#[derive(Clone)]
pub struct Py {
pub(crate) inner: chanlun::indicators::,
@@ -294,6 +302,7 @@ impl 相对强弱指数Py {
#[classmethod]
#[pyo3(signature = (初始收盘价, 初始时间, 周期 = None, 超买阈值 = None, 超卖阈值 = None, RSI_SMA周期 = None))]
/// 首次计算RSI(没有足够历史数据时使用)
fn (
_cls: &Bound<'_, PyType>,
: f64,
@@ -317,6 +326,7 @@ impl 相对强弱指数Py {
#[classmethod]
#[pyo3(signature = (k线, 计算方式, 周期 = None, 超买阈值 = None, 超卖阈值 = None, RSI_SMA周期 = None))]
/// :param k线: 原始K线
fn _K线(
_cls: &Bound<'_, PyType>,
k线: &Bound<'_, PyAny>,
@@ -340,6 +350,7 @@ impl 相对强弱指数Py {
}
#[classmethod]
/// 基于前一个RSI指标增量计算当前RSI
fn (
_cls: &Bound<'_, PyType>,
RSI: &Bound<'_, Py>,
@@ -356,6 +367,7 @@ impl 相对强弱指数Py {
}
#[classmethod]
/// :param 前一个RSI: 前一个RSI指标对象
fn _K线(
_cls: &Bound<'_, PyType>,
RSI: &Bound<'_, Py>,
@@ -390,7 +402,7 @@ impl 相对强弱指数Py {
/// 首次计算_K线(k线, 计算方式, ...)
/// 增量计算(前一个KDJ, 最高价, 最低价, 收盘价, 时间)
/// 增量计算_K线(前一个KDJ, 当前K线, 计算方式)
#[pyclass(name = "随机指标")]
#[pyclass(name = "随机指标", module = "chanlun._chanlun", from_py_object)]
#[derive(Clone)]
pub struct Py {
pub(crate) inner: chanlun::indicators::,
@@ -488,6 +500,7 @@ impl 随机指标Py {
#[classmethod]
#[pyo3(signature = (初始最高价, 初始最低价, 初始收盘价, 初始时间, N = None, M1 = None, M2 = None, 超买阈值 = None, 超卖阈值 = None))]
/// 首次计算KDJ(无历史数据时)
fn (
_cls: &Bound<'_, PyType>,
: f64,
@@ -517,6 +530,7 @@ impl 随机指标Py {
#[classmethod]
#[pyo3(signature = (k线, _计算方式, RSV周期 = None, K值平滑周期 = None, D值平滑周期 = None, 超买阈值 = None, 超卖阈值 = None))]
/// :param k线: 原始K线
fn _K线(
_cls: &Bound<'_, PyType>,
k线: &Bound<'_, PyAny>,
@@ -546,6 +560,7 @@ impl 随机指标Py {
}
#[classmethod]
/// 基于前一个KDJ对象和当前三价,增量计算当前KDJ值
fn (
_cls: &Bound<'_, PyType>,
KDJ: &Bound<'_, Py>,
@@ -566,6 +581,7 @@ impl 随机指标Py {
}
#[classmethod]
/// :param 前一个KDJ: 前一个KDJ指标对象
fn _K线(
_cls: &Bound<'_, PyType>,
KDJ: &Bound<'_, Py>,
@@ -595,12 +611,13 @@ impl 随机指标Py {
/// K线取值(k线, 指标计算方式) -> float (classmethod)
/// 根据计算方式从K线提取数值。
/// 计算方式: "收盘价" / "开盘价" / "高" / "低" / "均值" 等
#[pyclass(name = "指标")]
#[pyclass(name = "指标", module = "chanlun._chanlun")]
pub struct Py;
#[pymethods]
impl Py {
#[classmethod]
/// 根据计算方式从K线中取值
fn K线取值(
_cls: &Bound<'_, PyType>,
k线: &Bound<'_, PyAny>,
+73 -3
View File
@@ -23,7 +23,7 @@
*/
use pyo3::prelude::*;
use pyo3::types::{PyBytes, PyType};
use pyo3::types::{PyBytes, PyDict, PyType};
use std::collections::HashMap;
use std::rc::Rc;
@@ -52,7 +52,7 @@ use crate::types_py::相对方向Py;
/// 获取MACD(K线序列, 计算方式, 快线周期?, 慢线周期?, 信号周期?) -> list[平滑异同移动平均线]
/// — 对整个K线序列批量计算 MACD
/// 截取(序列, 起点K线, 终点K线) -> list — 按时间戳截取K线区间
#[pyclass(name = "K线", unsendable)]
#[pyclass(name = "K线", module = "chanlun._chanlun", unsendable)]
pub struct K线Py {
pub(crate) inner: Rc<chanlun::kline::bar::K线>,
}
@@ -172,6 +172,7 @@ impl K线Py {
}
#[getter]
/// :return: 相对方向.向上(开盘<收盘)或 相对方向.向下(开盘>收盘)
fn (&self) -> Py {
Py {
inner: self.inner.(),
@@ -202,6 +203,32 @@ impl K线Py {
.map(|k| Py { inner: k.clone() })
}
/// pandas 兼容 — 返回所有字段构成的字典
#[getter]
fn __dict__(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
let dict = PyDict::new(py);
dict.set_item("标识", self.())?;
dict.set_item("序号", self.())?;
dict.set_item("周期", self.())?;
dict.set_item("时间戳", self.())?;
dict.set_item("", self.())?;
dict.set_item("", self.())?;
dict.set_item("开盘价", self.())?;
dict.set_item("收盘价", self.())?;
dict.set_item("成交量", self.())?;
dict.set_item("方向", self.())?;
if let Some(v) = self.macd() {
dict.set_item("macd", v)?;
}
if let Some(v) = self.rsi() {
dict.set_item("rsi", v)?;
}
if let Some(v) = self.kdj() {
dict.set_item("kdj", v)?;
}
Ok(dict.into())
}
fn __str__(&self) -> String {
format!("{}", self.inner)
}
@@ -227,6 +254,7 @@ impl K线Py {
#[classmethod]
#[pyo3(signature = (标识, 时间戳, 开盘价, 最高价, 最低价, 收盘价, 成交量, 序号 = None, 周期 = None))]
/// 快捷构造普通K线
fn K(
_cls: &Bound<'_, PyType>,
: &str,
@@ -255,6 +283,7 @@ impl K线Py {
}
#[classmethod]
/// 将K线序列保存为二进制DAT文件
fn DAT文件(
_cls: &Bound<'_, PyType>,
: &str,
@@ -268,6 +297,7 @@ impl K线Py {
}
#[classmethod]
/// 从大端字节序二进制数据反序列化K线(兼容.dat/.nb文件格式)
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, PyBytes>,
@@ -282,6 +312,7 @@ impl K线Py {
}
#[classmethod]
/// 计算指定K线区间的MACD柱面积
fn MACD(
_cls: &Bound<'_, PyType>,
k线序列: Vec<Py<Self>>,
@@ -295,6 +326,7 @@ impl K线Py {
}
#[staticmethod]
/// 按起止K线截取K线子序列
fn (
: Vec<Py<Self>>,
: &Bound<'_, Self>,
@@ -348,7 +380,12 @@ impl K线Py {
/// 分析(缠K序列, 配置, 可以逆序包含?, 忽视顺序包含?, 可以逆序包含新?) -> (str, 分型|None)
/// — 分析分型形成结果
/// 截取(序列, 起点分型, 终点分型) -> list — 截取分型间的缠K子序列
#[pyclass(name = "缠论K线", unsendable)]
#[pyclass(
name = "缠论K线",
module = "chanlun._chanlun",
unsendable,
from_py_object
)]
pub struct K线Py {
pub(crate) inner: std::rc::Rc<chanlun::kline::chan_kline::K线>,
bsp_set: std::cell::RefCell<Option<Py<pyo3::types::PySet>>>,
@@ -445,6 +482,30 @@ impl 缠论K线Py {
}
}
/// pandas 兼容 — 返回所有字段构成的字典
#[getter]
fn __dict__(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
let dict = PyDict::new(py);
dict.set_item("序号", self.())?;
dict.set_item("时间戳", self.())?;
dict.set_item("", self.())?;
dict.set_item("", self.())?;
dict.set_item("方向", self.())?;
dict.set_item("周期", self.())?;
dict.set_item("标识", self.())?;
dict.set_item("分型特征值", self.())?;
dict.set_item("原始起始序号", self.())?;
dict.set_item("原始结束序号", self.())?;
dict.set_item("与MACD柱子匹配", self.MACD柱子匹配())?;
dict.set_item("与RSI匹配", self.RSI匹配())?;
dict.set_item("与KDJ匹配", self.KDJ匹配())?;
if let Some(v) = self.() {
dict.set_item("分型", v)?;
}
Ok(dict.into())
}
fn __str__(&self) -> String {
format!("{}", self.inner)
}
@@ -465,6 +526,7 @@ impl 缠论K线Py {
}
#[getter]
/// 创建当前缠K的浅拷贝副本
fn (&self, py: Python<'_>) -> Self {
let mut mirror = Self {
inner: std::rc::Rc::new(self.inner.()),
@@ -482,16 +544,19 @@ impl 缠论K线Py {
}
#[getter]
/// :return: 底分型时MACD柱<0,顶分型时MACD柱>0
fn MACD柱子匹配(&self) -> bool {
self.inner.MACD柱子匹配()
}
#[getter]
/// :return: 底分型时RSI < RSI_SMA,顶分型时RSI > RSI_SMA
fn RSI匹配(&self) -> bool {
self.inner.RSI匹配()
}
#[getter]
/// :return: 底分型时K<D,顶分型时K>D
fn KDJ匹配(&self) -> bool {
self.inner.KDJ匹配()
}
@@ -515,6 +580,7 @@ impl 缠论K线Py {
}
#[classmethod]
/// 在基线序列中找到与k线时间戳对齐的时间戳
fn (
_cls: &Bound<'_, PyType>,
线: Vec<Py<Self>>,
@@ -529,6 +595,7 @@ impl 缠论K线Py {
}
#[classmethod]
/// 创建新的缠论K线
fn K(
_cls: &Bound<'_, PyType>,
: i64,
@@ -556,6 +623,7 @@ impl 缠论K线Py {
}
#[classmethod]
/// K线包含处理(合并)
fn (
_cls: &Bound<'_, PyType>,
K: Option<&Bound<'_, Self>>,
@@ -578,6 +646,7 @@ impl 缠论K线Py {
}
#[classmethod]
/// 分析K线,执行指标计算+包含处理+分型判定
fn (
_cls: &Bound<'_, PyType>,
K线: &Bound<'_, K线Py>,
@@ -609,6 +678,7 @@ impl 缠论K线Py {
}
#[staticmethod]
/// :param 序列: 缠K序列
fn (
: Vec<Py<Self>>,
: &Bound<'_, Self>,
+1
View File
@@ -34,6 +34,7 @@ mod types_py;
/// 缠论技术分析库 — Rust 高性能实现
#[pymodule]
/// 缠论技术分析库 — Rust 高性能实现
fn _chanlun(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
// 阶段 1: 枚举和基础类型
types_py::register(m)?;
+120 -5
View File
@@ -23,7 +23,7 @@
*/
use pyo3::prelude::*;
use pyo3::types::PyType;
use pyo3::types::{PyDict, PyType};
use std::collections::HashMap;
use std::rc::Rc;
@@ -47,7 +47,7 @@ use crate::types_py::{分型结构Py, 相对方向Py, 缺口Py};
/// 判断分型(缠K序列, 索引, 配置) -> 分型|None — 在缠K序列中指定位置尝试创建分型
/// 从缠K序列中获取分型(缠K序列, 配置) -> list[分型] — 扫描全序列提取所有分型
/// 向序列中添加(分型序列, 新分型) — 维护分型序列的顺序一致性
#[pyclass(name = "分型", unsendable)]
#[pyclass(name = "分型", module = "chanlun._chanlun", unsendable, from_py_object)]
#[derive(Clone)]
pub struct Py {
pub(crate) inner: Rc<chanlun::structure::fractal_obj::>,
@@ -128,6 +128,7 @@ impl 分型Py {
}
#[getter]
/// 左、中、右三对相对方向关系
fn (&self) -> Option<(Py, Py, Py)> {
self.inner.().map(|(a, b, c)| {
(
@@ -139,17 +140,42 @@ impl 分型Py {
}
#[getter]
/// 分型强度(强/中/弱/未知)
fn (&self) -> String {
self.inner.().to_string()
}
#[getter]
/// :return: 底分型时左右MACD柱 > 中MACD柱,顶分型时左右MACD柱 < 中MACD柱
fn MACD柱子分型匹配(&self) -> bool {
self.inner.MACD柱子分型匹配()
}
/// pandas 兼容 — 返回所有字段构成的字典
#[getter]
fn __dict__(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
let dict = PyDict::new(py);
dict.set_item("结构", self.())?;
dict.set_item("时间戳", self.())?;
dict.set_item("分型特征值", self.())?;
dict.set_item("强度", self.())?;
dict.set_item("与MACD柱子分型匹配", self.MACD柱子分型匹配())?;
if let Some(v) = self.() {
dict.set_item("", v)?;
}
dict.set_item("", self.())?;
if let Some(v) = self.() {
dict.set_item("", v)?;
}
if let Some(v) = self.() {
dict.set_item("关系组", v)?;
}
Ok(dict.into())
}
#[classmethod]
#[pyo3(signature = (左, 右, 模式 = ""))]
/// 判断两个分型是否相同(identity比较)
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, Self>,
@@ -164,6 +190,7 @@ impl 分型Py {
}
#[staticmethod]
/// 从缠K序列中提取以指定缠K为中元素的分型
fn K序列中获取分型(
K线序列: Vec<Py<K线Py>>,
: &Bound<'_, K线Py>,
@@ -183,6 +210,7 @@ impl 分型Py {
}
#[staticmethod]
/// 向分型序列尾部添加,自动校验顶底交替
fn (
: &Bound<'_, PyAny>, : &Bound<'_, Self>
) -> PyResult<()> {
@@ -229,7 +257,7 @@ impl 分型Py {
/// 计算MACD柱子均值 / 武之全量MACD均值 / 武之MACD均值 / 武之MACD极值
/// 计算K线序列MACD趋向背驰 / 买卖意义 / 计算MACD柱子分段
/// 密集区域按间隔 / 统计MACD行为
#[pyclass(name = "虚线", unsendable)]
#[pyclass(name = "虚线", module = "chanlun._chanlun", unsendable, from_py_object)]
#[derive(Clone)]
pub struct 线Py {
pub(crate) inner: Rc<chanlun::structure::dash_line::线>,
@@ -401,6 +429,7 @@ impl 虚线Py {
// ---- 计算属性 ----
#[getter]
/// 笔序列
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for d in self.inner.() {
@@ -415,11 +444,13 @@ impl 虚线Py {
}
#[getter]
/// :return: 图表显示标题
fn (&self) -> String {
self.inner.()
}
#[getter]
/// :return: 运行方向
fn (&self) -> Py {
Py {
inner: self.inner.(),
@@ -427,23 +458,28 @@ impl 虚线Py {
}
#[getter]
/// 虚线区间的最高价(向上线段为终点分型最高价,向下线段为起点分型最高价)
fn (&self) -> f64 {
self.inner.()
}
#[getter]
/// 虚线区间的最低价(向下线段为终点分型最低价,向上线段为起点分型最低价)
fn (&self) -> f64 {
self.inner.()
}
/// :param 之前: 前一条虚线
fn (&self, : &Bound<'_, Self>) -> bool {
self.inner.(&.borrow().inner)
}
/// :param 之后: 后一条虚线
fn (&self, : &Bound<'_, Self>) -> bool {
self.inner.(&.borrow().inner)
}
/// :param 观察员: 观察者实例
fn K序列(
&self,
: &Bound<'_, crate::business_py::Py>,
@@ -460,6 +496,7 @@ impl 虚线Py {
Ok(list.into())
}
/// :param 观察员: 观察者实例
fn K序列(
&self,
: &Bound<'_, crate::business_py::Py>,
@@ -475,16 +512,34 @@ impl 虚线Py {
}
#[classmethod]
/// 递归获取虚线的终点分型(笔直接返回武,线段递归到底层笔的武)
fn _武(_cls: &Bound<'_, PyType>, 线: &Bound<'_, Self>) -> Py {
Py {
inner: 线.borrow().inner._武(),
}
}
/// 获取用于保存的数据文本
fn (&self) -> String {
self.inner.()
}
/// pandas 兼容 — 返回关键标量字段构成的字典
#[getter]
fn __dict__(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
let dict = PyDict::new(py);
dict.set_item("标识", self.())?;
dict.set_item("序号", self.())?;
dict.set_item("级别", self.())?;
dict.set_item("有效性", self.())?;
dict.set_item("模式", self.())?;
dict.set_item("短路修正", self.())?;
dict.set_item("图表标题", self.())?;
dict.set_item("文_时间戳", self.().())?;
dict.set_item("武_时间戳", self.().())?;
Ok(dict.into())
}
fn __str__(&self) -> String {
format!("{}", self.inner)
}
@@ -507,6 +562,7 @@ impl 虚线Py {
// ---- 静态工厂方法 ----
#[classmethod]
/// :param 文: 起点分型
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, Py>,
@@ -523,6 +579,7 @@ impl 虚线Py {
}
#[classmethod]
/// :param 虚线序列: 构成线段的虚线列表(笔)
fn 线(_cls: &Bound<'_, PyType>, 线: Vec<Py<Self>>, py: Python<'_>) -> Self {
let rc_list: Vec<Rc<chanlun::structure::dash_line::线>> = 线
.iter()
@@ -538,6 +595,7 @@ impl 虚线Py {
// ---- 买卖点模式匹配 ----
#[classmethod]
/// :param 模式: "全量"/"任意"/"配置"/"相对"
fn K买卖点模式(
_cls: &Bound<'_, PyType>,
: &str,
@@ -554,6 +612,7 @@ impl 虚线Py {
}
#[classmethod]
/// 根据配置中的指标开关检测缠K匹配情况(MACD/KDJ/RSI组合)
fn (
_cls: &Bound<'_, PyType>,
K: &Bound<'_, K线Py>,
@@ -568,16 +627,19 @@ impl 虚线Py {
}
#[classmethod]
/// :param 缠K: 缠论K线
fn (_cls: &Bound<'_, PyType>, K: &Bound<'_, K线Py>) -> bool {
chanlun::structure::dash_line::线::(&K.borrow().inner)
}
#[classmethod]
/// :param 缠K: 缠论K线
fn (_cls: &Bound<'_, PyType>, K: &Bound<'_, K线Py>) -> bool {
chanlun::structure::dash_line::线::(&K.borrow().inner)
}
#[classmethod]
/// :param 缠K: 缠论K线
fn (_cls: &Bound<'_, PyType>, K: &Bound<'_, K线Py>) -> bool {
chanlun::structure::dash_line::线::(&K.borrow().inner)
}
@@ -585,6 +647,7 @@ impl 虚线Py {
// ---- MACD 相关 classmethods ----
#[classmethod]
/// :param 普K序列: 完整K线序列
fn MACD柱子均值(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<K线Py>>,
@@ -602,6 +665,7 @@ impl 虚线Py {
}
#[classmethod]
/// :param 普K序列: 完整K线序列
fn MACD均值(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<K线Py>>,
@@ -619,6 +683,7 @@ impl 虚线Py {
}
#[classmethod]
/// :param 普K序列: 完整K线序列
fn MACD均值(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<K线Py>>,
@@ -633,6 +698,7 @@ impl 虚线Py {
}
#[classmethod]
/// :param 普K序列: 完整K线序列
fn MACD极值(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<K线Py>>,
@@ -647,6 +713,7 @@ impl 虚线Py {
}
#[classmethod]
/// :param 普K序列: 完整K线序列
fn MACD柱子均值_阴(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<K线Py>>,
@@ -664,6 +731,7 @@ impl 虚线Py {
}
#[classmethod]
/// :param 普K序列: 完整K线序列
fn MACD柱子均值_阳(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<K线Py>>,
@@ -681,6 +749,7 @@ impl 虚线Py {
}
#[classmethod]
/// :param 普K序列: 完整K线序列
fn MACD均值_阴(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<K线Py>>,
@@ -695,6 +764,7 @@ impl 虚线Py {
}
#[classmethod]
/// :param 普K序列: 完整K线序列
fn MACD均值_阳(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<K线Py>>,
@@ -709,6 +779,7 @@ impl 虚线Py {
}
#[classmethod]
/// 计算K线序列的MACD柱/DIF/DEA趋向背驰(三元素判断)
fn K线序列MACD趋向背驰(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<K线Py>>,
@@ -726,6 +797,7 @@ impl 虚线Py {
}
#[classmethod]
/// :param k线序列: K线序列
fn MACD柱子分段(
_cls: &Bound<'_, PyType>,
k线序列: Vec<Py<K线Py>>,
@@ -739,6 +811,7 @@ impl 虚线Py {
}
#[classmethod]
/// 交叉标记: 长度为len(macd_list)的列表,0=无交叉, 1=金叉, -1=死叉
fn (
_cls: &Bound<'_, PyType>,
: Vec<i32>,
@@ -753,6 +826,7 @@ impl 虚线Py {
}
#[classmethod]
/// :param 普K序列: K线序列
fn MACD行为(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<K线Py>>,
@@ -793,6 +867,7 @@ impl 虚线Py {
}
#[classmethod]
/// 静止是相对的,而运动是绝对的
fn (
_cls: &Bound<'_, PyType>,
线: &Bound<'_, Self>,
@@ -823,7 +898,12 @@ impl 虚线Py {
/// 新建(序号, 文, 武, 基础序列?) -> 线段特征
/// 静态分析(虚线序列, 配置) -> 线段特征|None
/// 获取分型序列(虚线序列, 配置) -> list[线段特征]
#[pyclass(name = "线段特征", unsendable)]
#[pyclass(
name = "线段特征",
module = "chanlun._chanlun",
unsendable,
from_py_object
)]
#[derive(Clone)]
pub struct 线Py {
pub(crate) inner: Rc<chanlun::structure::segment_feat::线>,
@@ -939,14 +1019,27 @@ impl 线段特征Py {
Rc::as_ptr(&self.inner) as u64
}
/// pandas 兼容 — 返回关键标量字段构成的字典
#[getter]
fn __dict__(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
let dict = PyDict::new(py);
dict.set_item("序号", self.())?;
dict.set_item("标识", self.())?;
dict.set_item("线段方向", self.线())?;
dict.set_item("图表标题", self.())?;
Ok(dict.into())
}
// ---- instance methods ----
#[getter]
/// :return: 图表标题
fn (&self) -> String {
self.inner.()
}
#[getter]
/// 起点分型(向上线段取高高中的最大者,向下线段取低低中的最小者)
fn (&self) -> Py {
Py {
inner: self.inner.(),
@@ -954,6 +1047,7 @@ impl 线段特征Py {
}
#[getter]
/// 终点分型(向上线段取高高中的最大者,向下线段取低低中的最小者)
fn (&self) -> Py {
Py {
inner: self.inner.(),
@@ -961,6 +1055,7 @@ impl 线段特征Py {
}
#[getter]
/// :return: 特征序列方向(线段方向的翻转)
fn (&self) -> Py {
Py {
inner: self.inner.(),
@@ -968,15 +1063,18 @@ impl 线段特征Py {
}
#[getter]
/// :return: 文和武中分型特征值的较大者
fn (&self) -> f64 {
self.inner.()
}
#[getter]
/// :return: 文和武中分型特征值的较小者
fn (&self) -> f64 {
self.inner.()
}
/// :param 待添加虚线: 待添加的虚线
fn (&mut self, 线: &Bound<'_, 线Py>) -> PyResult<()> {
let inner = Rc::make_mut(&mut self.inner);
inner
@@ -984,6 +1082,7 @@ impl 线段特征Py {
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e))
}
/// :param 待删除虚线: 待删除的虚线
fn (&mut self, 线: &Bound<'_, 线Py>) -> PyResult<()> {
let inner = Rc::make_mut(&mut self.inner);
inner
@@ -994,6 +1093,7 @@ impl 线段特征Py {
// ---- classmethods ----
#[classmethod]
/// :param 虚线序列: 基础虚线列表
fn (
_cls: &Bound<'_, PyType>,
线: Vec<Py<线Py>>,
@@ -1013,6 +1113,7 @@ impl 线段特征Py {
}
#[classmethod]
/// 静态分析虚线序列,生成特征序列
fn (
_cls: &Bound<'_, PyType>,
线: Vec<Py<线Py>>,
@@ -1037,6 +1138,7 @@ impl 线段特征Py {
}
#[classmethod]
/// 从特征序列提取特征分型序列
fn (
_cls: &Bound<'_, PyType>,
: Vec<Py<Self>>,
@@ -1062,7 +1164,12 @@ impl 线段特征Py {
/// 属性 (只读):
/// 左: 线段特征|None / 中: 线段特征 / 右: 线段特征|None
/// 结构: 分型结构 — 顶/底分型判定结果
#[pyclass(name = "特征分型", unsendable)]
#[pyclass(
name = "特征分型",
module = "chanlun._chanlun",
unsendable,
from_py_object
)]
#[derive(Clone)]
pub struct Py {
pub(crate) inner: Rc<chanlun::structure::feat_fractal::>,
@@ -1115,6 +1222,14 @@ impl 特征分型Py {
}
}
/// pandas 兼容 — 返回关键标量字段构成的字典
#[getter]
fn __dict__(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
let dict = PyDict::new(py);
dict.set_item("结构", self.())?;
Ok(dict.into())
}
fn __str__(&self) -> String {
format!("{}", self.inner)
}
+130 -23
View File
@@ -22,8 +22,9 @@
* SOFTWARE.
*/
use pyo3::basic::CompareOp;
use pyo3::prelude::*;
use pyo3::types::PyType;
use pyo3::types::{PyDict, PyType};
// ========== 买卖点类型 ==========
@@ -37,7 +38,7 @@ use pyo3::types::PyType;
/// 属性:
/// 是买点: bool — 是否为买入类型
/// 是卖点: bool — 是否为卖出类型
#[pyclass(name = "买卖点类型")]
#[pyclass(name = "买卖点类型", module = "chanlun._chanlun", from_py_object)]
#[derive(Clone)]
pub struct Py {
pub inner: chanlun::types::,
@@ -53,14 +54,19 @@ impl 买卖点类型Py {
self.inner.to_string()
}
fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
if let Ok(s) = other.extract::<String>() {
return self.inner.to_string() == s;
fn __richcmp__(&self, other: &Bound<'_, PyAny>, op: CompareOp) -> PyResult<bool> {
let eq = if let Ok(s) = other.extract::<String>() {
self.inner.to_string() == s
} else if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
self.inner == other.inner
} else {
return Err(pyo3::exceptions::PyNotImplementedError::new_err(""));
};
match op {
CompareOp::Eq => Ok(eq),
CompareOp::Ne => Ok(!eq),
_ => Err(pyo3::exceptions::PyNotImplementedError::new_err("")),
}
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
return self.inner == other.inner;
}
false
}
fn __hash__(&self) -> u64 {
@@ -71,14 +77,25 @@ impl 买卖点类型Py {
}
#[getter]
/// 判断是否为买入类型(名称中含"买"字)
fn (&self) -> bool {
self.inner.()
}
#[getter]
/// 判断是否为卖出类型(名称中含"卖"字)
fn (&self) -> bool {
self.inner.()
}
/// pickle 支持 — 通过 getattr(类, 名称) 重建实例
fn __reduce__(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let getattr = py.import("builtins")?.getattr("getattr")?;
let module = py.import("chanlun._chanlun")?;
let cls = module.getattr("买卖点类型")?;
let name = self.__str__();
Ok((getattr, (cls, name)).into_pyobject(py)?.unbind().into())
}
}
// ========== 相对方向 ==========
@@ -101,7 +118,7 @@ impl 买卖点类型Py {
/// 方法:
/// 翻转() -> 相对方向 — 返回方向的对立面(向上↔向下, 缺口↔反向缺口)
/// 分析(前高, 前低, 后高, 后低) -> 相对方向 (classmethod) — 根据价格区间判断方向
#[pyclass(name = "相对方向")]
#[pyclass(name = "相对方向", module = "chanlun._chanlun", from_py_object)]
#[derive(Clone)]
pub struct Py {
pub inner: chanlun::types::,
@@ -117,11 +134,19 @@ impl 相对方向Py {
format!("{}", self.inner)
}
fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
return self.inner == other.inner;
fn __richcmp__(&self, other: &Bound<'_, PyAny>, op: CompareOp) -> PyResult<bool> {
let Ok(other) = other.extract::<PyRef<'_, Self>>() else {
return Err(pyo3::exceptions::PyNotImplementedError::new_err(""));
};
let eq = self.inner == other.inner;
match op {
CompareOp::Eq => Ok(eq),
CompareOp::Ne => Ok(!eq),
CompareOp::Lt => Ok((self.inner as u8) < (other.inner as u8)),
CompareOp::Le => Ok((self.inner as u8) <= (other.inner as u8)),
CompareOp::Gt => Ok((self.inner as u8) > (other.inner as u8)),
CompareOp::Ge => Ok((self.inner as u8) >= (other.inner as u8)),
}
false
}
fn __hash__(&self) -> u64 {
@@ -135,26 +160,44 @@ impl 相对方向Py {
}
}
/// 判断是否为向上方向(向上/向上缺口/衔接向上)
fn (&self) -> bool {
self.inner.()
}
/// 判断是否为向下方向(向下/向下缺口/衔接向下)
fn (&self) -> bool {
self.inner.()
}
/// 判断是否为包含关系(顺/逆/同)
fn (&self) -> bool {
self.inner.()
}
/// 判断是否有缺口(向下缺口/向上缺口)
fn (&self) -> bool {
self.inner.()
}
/// 判断是否为首尾衔接(衔接向下/衔接向上)
fn (&self) -> bool {
self.inner.()
}
/// pickle 支持 — 通过 getattr(类, 名称) 重建实例
fn __reduce__(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let getattr = py.import("builtins")?.getattr("getattr")?;
let module = py.import("chanlun._chanlun")?;
let cls = module.getattr("相对方向")?;
let full_name = self.__str__();
let name = full_name
.rsplit_once('.')
.map(|(_, v)| v)
.unwrap_or(&full_name);
Ok((getattr, (cls, name)).into_pyobject(py)?.unbind().into())
}
/// 根据前后价格区间的OHLC值分析方向关系。
///
/// 参数:
@@ -188,7 +231,7 @@ impl 相对方向Py {
/// 方法:
/// 分析(左, 中, 右, 可以逆序包含?, 忽视顺序包含?) -> 分型结构|None (classmethod)
/// — 根据三根K线的高低价分析分型结构
#[pyclass(name = "分型结构")]
#[pyclass(name = "分型结构", module = "chanlun._chanlun", from_py_object)]
#[derive(Clone)]
pub struct Py {
pub inner: chanlun::types::,
@@ -204,17 +247,34 @@ impl 分型结构Py {
self.inner.to_string()
}
fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
return self.inner == other.inner;
fn __richcmp__(&self, other: &Bound<'_, PyAny>, op: CompareOp) -> PyResult<bool> {
let Ok(other) = other.extract::<PyRef<'_, Self>>() else {
return Err(pyo3::exceptions::PyNotImplementedError::new_err(""));
};
let eq = self.inner == other.inner;
match op {
CompareOp::Eq => Ok(eq),
CompareOp::Ne => Ok(!eq),
CompareOp::Lt => Ok((self.inner as u8) < (other.inner as u8)),
CompareOp::Le => Ok((self.inner as u8) <= (other.inner as u8)),
CompareOp::Gt => Ok((self.inner as u8) > (other.inner as u8)),
CompareOp::Ge => Ok((self.inner as u8) >= (other.inner as u8)),
}
false
}
fn __hash__(&self) -> u64 {
self.inner as u64
}
/// pickle 支持 — 通过 getattr(类, 名称) 重建实例
fn __reduce__(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let getattr = py.import("builtins")?.getattr("getattr")?;
let module = py.import("chanlun._chanlun")?;
let cls = module.getattr("分型结构")?;
let name = self.__str__();
Ok((getattr, (cls, name)).into_pyobject(py)?.unbind().into())
}
/// 根据左中右三根K线的高/低价分析分型结构。
///
/// 参数:
@@ -297,7 +357,7 @@ impl 分型结构Py {
/// 方法:
/// 居中截取区间(起点, 终点, 比例=0.15) -> 缺口|None (classmethod)
/// — 在两个价格之间截取中间区域作为缺口
#[pyclass(name = "缺口")]
#[pyclass(name = "缺口", module = "chanlun._chanlun", from_py_object)]
#[derive(Clone)]
pub struct Py {
pub inner: chanlun::types::,
@@ -325,6 +385,34 @@ impl 缺口Py {
format!("{}", self.inner)
}
fn __richcmp__(&self, other: &Bound<'_, PyAny>, op: CompareOp) -> PyResult<bool> {
let Ok(other) = other.extract::<PyRef<'_, Self>>() else {
return Err(pyo3::exceptions::PyNotImplementedError::new_err(""));
};
match op {
CompareOp::Eq => Ok(self.inner. == other.inner. && self.inner. == other.inner.),
CompareOp::Ne => {
Ok(!(self.inner. == other.inner. && self.inner. == other.inner.))
}
CompareOp::Lt => Ok(self.inner. < other.inner.
|| (self.inner. == other.inner. && self.inner. < other.inner.)),
CompareOp::Le => Ok(self.inner. < other.inner.
|| (self.inner. == other.inner. && self.inner. <= other.inner.)),
CompareOp::Gt => Ok(self.inner. > other.inner.
|| (self.inner. == other.inner. && self.inner. > other.inner.)),
CompareOp::Ge => Ok(self.inner. > other.inner.
|| (self.inner. == other.inner. && self.inner. >= other.inner.)),
}
}
fn __hash__(&self) -> u64 {
use std::hash::{Hash, Hasher};
let mut h = std::collections::hash_map::DefaultHasher::new();
self.inner..to_bits().hash(&mut h);
self.inner..to_bits().hash(&mut h);
h.finish()
}
#[getter]
#[pyo3(name = "")]
fn get_高(&self) -> f64 {
@@ -349,6 +437,16 @@ impl 缺口Py {
self.inner. = value;
}
/// pickle 支持 — 通过 cls(高, 低) 重建实例
fn __reduce__(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let module = py.import("chanlun._chanlun")?;
let cls = module.getattr("缺口")?;
Ok((cls, (self.inner., self.inner.))
.into_pyobject(py)?
.unbind()
.into())
}
/// 在两个价格之间截取中间区域作为缺口。
///
/// 参数:
@@ -404,10 +502,13 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
("T3B卖", chanlun::types::::T3B卖),
];
let mut bsp_members = PyDict::new(py);
for (name, value) in variants {
let instance = Py::new(py, Py { inner: *value })?;
bsp_class.setattr(*name, instance)?;
bsp_class.setattr(*name, instance.clone_ref(py))?;
bsp_members.set_item(*name, instance)?;
}
bsp_class.setattr("__members__", bsp_members)?;
// 相对方向 class attributes
let dir_class = m.getattr("相对方向")?.downcast_into::<PyType>()?.clone();
@@ -423,10 +524,13 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
("", chanlun::types::::),
];
let mut dir_members = PyDict::new(py);
for (name, value) in dir_variants {
let instance = Py::new(py, Py { inner: *value })?;
dir_class.setattr(*name, instance)?;
dir_class.setattr(*name, instance.clone_ref(py))?;
dir_members.set_item(*name, instance)?;
}
dir_class.setattr("__members__", dir_members)?;
// 分型结构 class attributes
let frac_class = m.getattr("分型结构")?.downcast_into::<PyType>()?.clone();
@@ -438,10 +542,13 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
("", chanlun::types::::),
];
let mut frac_members = PyDict::new(py);
for (name, value) in frac_variants {
let instance = Py::new(py, Py { inner: *value })?;
frac_class.setattr(*name, instance)?;
frac_class.setattr(*name, instance.clone_ref(py))?;
frac_members.set_item(*name, instance)?;
}
frac_class.setattr("__members__", frac_members)?;
// Register module-level functions
m.add_function(wrap_pyfunction!(, m)?)?;
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 YuYuKunKun
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.