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:
@@ -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"
|
||||
|
||||
@@ -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
@@ -1,3 +1,33 @@
|
||||
"""缠论技术分析库 — Rust 高性能实现"""
|
||||
|
||||
__all__ = [
|
||||
"K线",
|
||||
"K线合成器",
|
||||
"中枢",
|
||||
"买卖点",
|
||||
"买卖点类型",
|
||||
"分型",
|
||||
"分型结构",
|
||||
"基础买卖点",
|
||||
"平滑异同移动平均线",
|
||||
"指标",
|
||||
"测试_读取数据",
|
||||
"特征分型",
|
||||
"相对强弱指数",
|
||||
"相对方向",
|
||||
"立体分析器",
|
||||
"笔",
|
||||
"线段",
|
||||
"线段特征",
|
||||
"缠论K线",
|
||||
"缠论配置",
|
||||
"缺口",
|
||||
"背驰分析",
|
||||
"虚线",
|
||||
"观察者",
|
||||
"转化为时间戳",
|
||||
"转化为时间戳_数字",
|
||||
"随机指标",
|
||||
]
|
||||
|
||||
from ._chanlun import *
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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" }
|
||||
|
||||
@@ -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>>,
|
||||
|
||||
@@ -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.测试_保存数据();
|
||||
}
|
||||
|
||||
@@ -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<'_>,
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -34,6 +34,7 @@ mod types_py;
|
||||
|
||||
/// 缠论技术分析库 — Rust 高性能实现
|
||||
#[pymodule]
|
||||
/// 缠论技术分析库 — Rust 高性能实现
|
||||
fn _chanlun(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
// 阶段 1: 枚举和基础类型
|
||||
types_py::register(m)?;
|
||||
|
||||
@@ -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
@@ -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)?)?;
|
||||
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user