Files
chanlun.rs/chanlun/src/structure/dash_line.rs
T
YuWuKunCheng da5222d960 添加 全局变量“扩展线段模式” 默认为 True
线段 扩展分析采用“端点高, 端点低” 作为高低取值。中枢分析时高低取值不变,此举在中枢<扩展**> 中更贴合原文同级别分级时的中枢高低取值
2026-06-10 23:21:33 +08:00

1767 lines
66 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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.
*/
use crate::algorithm::hub::中枢;
use crate::config::缠论配置;
use crate::kline::bar::K线;
use crate::kline::chan_kline::缠论K线;
use crate::structure::fractal_obj::分型;
use crate::structure::segment_feat::线段特征;
use crate::types::{分型结构, 相对方向, 缺口};
use cached::stores::LruCache;
use std::sync::atomic::{AtomicBool, AtomicI64, Ordering};
use std::sync::{Arc, LazyLock, Mutex, RwLock};
use tracing::warn;
/// 扩展线段模式 — 控制虚线高低取值方式
///
/// 默认 `true`:扩展线段取基础序列中所有子段端点的全局极值(与原文54课一致)
/// 设为 `true`:使用文/武分型端点
pub static 扩展线段模式: AtomicBool = AtomicBool::new(true);
/// 虚线 — 笔和线段的通用数据结构。
///
/// 笔和线段共享此 struct,通过 `标识` 字段区分("笔"/"线段"/"扩展线段"等)。
/// 文=起点分型,武=终点分型,方向由文→武的结构关系判定(顶→底=向下,底→顶=向上)。
///
/// 字段:
/// 标识: "笔" / "线段" / "扩展线段" / "线段<线段>" 等
/// 序号: 在同级序列中的编号
/// 级别: 层级深度(笔=1, 线段=2, 线段<线段>=3 ...
/// 文: 起点分型(不可变,确保指针身份一致)
/// 武: 终点分型(可变,用于动态更新终点)
/// 有效性: 该虚线是否有效
/// 基础序列: 构成该虚线的子级虚线序列(笔→空;线段→笔序列;线段<线段>→线段序列 ...
/// 特征序列: 线段特征序列(笔为空;线段为[None, None, None]初始化)
/// 实_中枢序列: 实中枢列表(根据配置识别的中枢)
/// 虚_中枢序列: 虚中枢列表(忽略配置约束的中枢)
/// 合_中枢序列: 合并中枢列表(实+虚+扩展)
/// 确认K线: 线段被破坏时的确认K线
/// 模式: 买卖点匹配模式("文武"/"全量"/"任意"/"配置"/"相对"
/// _特征序列_显示: 是否显示特征序列(图表渲染用)
/// 前一缺口: 特征序列第一二元素间的缺口
/// 前一结束位置: 前一虚线段结束时所在的位置
/// 短路修正: 是否启用短路修正(线段短路过时的紧急处理标记)
#[derive(Debug)]
pub struct 虚线 {
/// 标识字符串: "笔" / "线段" / "扩展线段" / "线段<线段>" 等
pub 标识: RwLock<String>,
/// 同级序列中的编号
pub 序号: AtomicI64,
/// 层级深度(笔=1, 线段=2)
pub 级别: AtomicI64,
/// 起点分型(不可变)
pub : Arc<分型>,
/// 终点分型(可变)
pub : RwLock<Arc<分型>>,
/// 是否有效
pub 有效性: AtomicBool,
/// 构成该虚线的子级虚线序列
pub 基础序列: RwLock<Vec<Arc<虚线>>>,
/// 线段特征序列
pub 特征序列: RwLock<Vec<Option<Arc<线段特征>>>>,
/// 实中枢列表
pub _中枢序列: RwLock<Vec<Arc<中枢>>>,
/// 虚中枢列表
pub _中枢序列: RwLock<Vec<Arc<中枢>>>,
/// 合并中枢列表(实+虚+扩展)
pub _中枢序列: RwLock<Vec<Arc<中枢>>>,
/// 线段被破坏时的确认K线
pub 确认K线: RwLock<Option<Arc<缠论K线>>>,
/// 买卖点匹配模式
pub 模式: RwLock<String>,
/// 是否显示特征序列(图表渲染)
pub _特征序列_显示: AtomicBool,
/// 特征序列第一二元素间的缺口
pub 前一缺口: RwLock<Option<缺口>>,
/// 前一虚线段结束位置
pub 前一结束位置: RwLock<Option<Arc<虚线>>>,
/// 短路紧急修正标记
pub 短路修正: AtomicBool,
}
/// MACD行为统计 — `统计MACD行为` 方法的返回类型,汇总MACD穿轴和交叉信息。
///
/// 字段:
/// DIF上穿0: DIF从负穿正的次数
/// DIF下穿0: DIF从正穿负的次数
/// DEA上穿0: DEA从负穿正的次数
/// DEA下穿0: DEA从正穿负的次数
/// 金叉次数: DIF上穿DEA的次数
/// 死叉次数: DIF下穿DEA的次数
/// 密集交叉区域: (起始索引, 结束索引, 交叉次数) 列表
#[derive(Debug, Clone)]
pub struct MACD行为统计 {
/// DIF从负穿正的次数
pub DIF上穿0: i64,
/// DIF从正穿负的次数
pub DIF下穿0: i64,
/// DEA从负穿正的次数
pub DEA上穿0: i64,
/// DEA从正穿负的次数
pub DEA下穿0: i64,
/// DIF上穿DEA的次数
pub 金叉次数: i64,
/// DIF下穿DEA的次数
pub 死叉次数: i64,
/// 密集交叉区域 (起始索引, 结束索引, 交叉次数)
pub 密集交叉区域: Vec<(usize, usize, usize)>,
}
impl Clone for 虚线 {
fn clone(&self) -> Self {
Self {
标识: RwLock::new(self.标识.read().unwrap().clone()),
序号: AtomicI64::new(self.序号.load(Ordering::Relaxed)),
级别: AtomicI64::new(self.级别.load(Ordering::Relaxed)),
: Arc::clone(&self.),
: RwLock::new(Arc::clone(&self..read().unwrap())),
有效性: AtomicBool::new(self.有效性.load(Ordering::Relaxed)),
基础序列: RwLock::new(self.基础序列.read().unwrap().clone()),
特征序列: RwLock::new(self.特征序列.read().unwrap().clone()),
_中枢序列: RwLock::new(self._中枢序列.read().unwrap().clone()),
_中枢序列: RwLock::new(self._中枢序列.read().unwrap().clone()),
_中枢序列: RwLock::new(self._中枢序列.read().unwrap().clone()),
确认K线: RwLock::new(self.确认K线.read().unwrap().clone()),
模式: RwLock::new(self.模式.read().unwrap().clone()),
_特征序列_显示: AtomicBool::new(self._特征序列_显示.load(Ordering::Relaxed)),
前一缺口: RwLock::new(*self.前一缺口.read().unwrap()),
前一结束位置: RwLock::new(self.前一结束位置.read().unwrap().clone()),
短路修正: AtomicBool::new(self.短路修正.load(Ordering::Relaxed)),
}
}
}
type 买卖意义缓存类型 = LruCache<(usize, usize), (bool, String)>;
/// 买卖意义 LRU 缓存(max 128 条目,对齐 Python @lru_cache(maxsize=128)
static 买卖意义缓存: LazyLock<Mutex<买卖意义缓存类型>> =
LazyLock::new(|| Mutex::new(LruCache::with_size(128)));
impl 虚线 {
pub fn new(
序号: i64,
标识: String,
: Arc<分型>,
: Arc<分型>,
级别: i64,
有效性: bool,
) -> Self {
Self {
序号: AtomicI64::new(序号),
标识: RwLock::new(标识),
级别: AtomicI64::new(级别),
,
: RwLock::new(),
有效性: AtomicBool::new(有效性),
基础序列: RwLock::new(Vec::new()),
特征序列: RwLock::new(vec![None, None, None]),
_中枢序列: RwLock::new(Vec::new()),
_中枢序列: RwLock::new(Vec::new()),
_中枢序列: RwLock::new(Vec::new()),
确认K线: RwLock::new(None),
模式: RwLock::new("文武".into()),
_特征序列_显示: AtomicBool::new(false),
前一缺口: RwLock::new(None),
前一结束位置: RwLock::new(None),
短路修正: AtomicBool::new(false),
}
}
/// 图表标题 — 格式为 "符号:周期:标识:序号"
pub fn 图表标题(&self) -> String {
format!(
"{}:{}:{}:{}",
self...标识,
self...周期,
self.标识.read().unwrap(),
self.序号.load(Ordering::Relaxed)
)
}
/// 方向 — 文到武的方向(对齐 Python:无法识别时 panic
pub fn 方向(&self) -> 相对方向 {
match (self..结构, self..read().unwrap().结构) {
(分型结构::, 分型结构::) => 相对方向::向下,
(分型结构::, 分型结构::) => 相对方向::向下,
(分型结构::, 分型结构::) => 相对方向::向上,
(分型结构::, 分型结构::) => 相对方向::向上,
_ => panic!(
"虚线 方向 无法识别: 文.结构={:?}, 武.结构={:?}",
self..结构,
self..read().unwrap().结构
),
}
}
/// 虚线端点高 — 始终取文/武分型端点的中.高,不扫描基础序列
pub fn 端点高(&self) -> f64 {
if self.方向() == 相对方向::向上 {
self..read().unwrap()...get()
} else {
self....get()
}
}
/// 虚线端点低 — 始终取文/武分型端点的中.低,不扫描基础序列
pub fn 端点低(&self) -> f64 {
if self.方向() == 相对方向::向下 {
self..read().unwrap()...get()
} else {
self....get()
}
}
/// 虚线高
/// 默认同端点高;扩展线段模式开启时,取基础序列中所有子段文分型 + 最后子段武分型的最高
pub fn (&self) -> f64 {
let 模式 = self.模式.read().unwrap();
let 标识 = self.标识.read().unwrap();
if 扩展线段模式.load(Ordering::Relaxed)
&& *模式 != "文武"
&& *标识 != "笔"
&& 标识.contains("扩展")
{
let 基础序列 = self.基础序列.read().unwrap();
if !基础序列.is_empty() {
return 基础序列
.iter()
.map(|s| s....get())
.chain(std::iter::once(
基础序列.last().unwrap()..read().unwrap()...get(),
))
.fold(f64::NEG_INFINITY, f64::max);
}
}
self.端点高()
}
/// 虚线低
/// 默认同端点低;扩展线段模式开启时,取基础序列中所有子段文分型 + 最后子段武分型的最低
pub fn (&self) -> f64 {
let 模式 = self.模式.read().unwrap();
let 标识 = self.标识.read().unwrap();
if 扩展线段模式.load(Ordering::Relaxed)
&& *模式 != "文武"
&& *标识 != "笔"
&& 标识.contains("扩展")
{
let 基础序列 = self.基础序列.read().unwrap();
if !基础序列.is_empty() {
return 基础序列
.iter()
.map(|s| s....get())
.chain(std::iter::once(
基础序列.last().unwrap()..read().unwrap()...get(),
))
.fold(f64::INFINITY, f64::min);
}
}
self.端点低()
}
/// 判断两个虚线是否首尾相连
pub fn 之前是(&self, 之前: &虚线) -> bool {
if *self.标识.read().unwrap() != *之前.标识.read().unwrap() {
return false;
}
Arc::as_ptr(&*之前..read().unwrap()) == Arc::as_ptr(&self.)
}
/// 判断两个虚线是否首尾相连
pub fn 之后是(&self, 之后: &虚线) -> bool {
if *self.标识.read().unwrap() != *之后.标识.read().unwrap() {
return false;
}
Arc::as_ptr(&*self..read().unwrap()) == Arc::as_ptr(&之后.)
}
/// 获取该虚线范围内的普K序列
/// 对齐说明:Python 接收 观察者 对象,Rust 直接接收切片引用,行为等价
pub fn 获取普K序列(&self, K序列: &[Arc<K线>]) -> Vec<Arc<K线>> {
// 使用指针查找(与 Python list.index 身份匹配行为一致),
// 而非序号切片——因为序号可能与实际位置不一致。
let = K序列
.iter()
.position(|k| Arc::as_ptr(k) == Arc::as_ptr(&*self...标的K线.read().unwrap()));
let = K序列.iter().position(|k| {
Arc::as_ptr(k) == Arc::as_ptr(&*self..read().unwrap()..标的K线.read().unwrap())
});
match (, ) {
(Some(s), Some(e)) if s <= e => K序列[s..=e].to_vec(),
_ => {
// 指针查找失败时回退到序号方式
warn!("[警告]虚线.获取普K序列 <指针查找失败时回退到序号方式>");
let = self...原始起始序号 as usize;
let = self
.
.read()
.unwrap()
.
.原始结束序号
.load(Ordering::Relaxed) as usize;
if < K序列.len() && < K序列.len() && <= {
K序列[..=].to_vec()
} else {
Vec::new()
}
}
}
}
/// 获取该虚线范围内的缠K序列
/// 对齐说明:Python 接收 观察者 对象,Rust 直接接收切片引用,行为等价
pub fn 获取缠K序列(&self, K序列: &[Arc<缠论K线>]) -> Vec<Arc<缠论K线>> {
缠论K线::截取(K序列, &self.., &self..read().unwrap().).unwrap_or_default()
}
/// 获取_武 — 递归获取虚线的终点分型(笔直接返回武,线段递归到底层笔的武)
/// 对齐说明:Python 是 @classmethodcls, 实线),Rust 是实例方法(&self),逻辑等价
pub fn 获取_武(&self) -> Arc<分型> {
if *self.标识.read().unwrap() == "笔" {
return self..read().unwrap().clone();
}
let mut current_rc = Arc::clone(self.基础序列.read().unwrap().last().unwrap());
loop {
if *current_rc.标识.read().unwrap() == "笔" {
return current_rc..read().unwrap().clone();
}
let next = Arc::clone(current_rc.基础序列.read().unwrap().last().unwrap());
current_rc = next;
}
}
/// 获取数据文本(用于保存/调试)
pub fn 获取数据文本(&self) -> String {
use crate::utils::format_f64_g;
if *self.标识.read().unwrap() == "笔" {
return format!(
"{}, {}, {}, 文:({},{}), 武:({},{}), {}",
self.标识.read().unwrap(),
self.序号.load(Ordering::Relaxed),
self.级别.load(Ordering::Relaxed),
self..时间戳(),
format_f64_g(self..分型特征值),
self..read().unwrap().时间戳(),
format_f64_g(self..read().unwrap().分型特征值),
if self.有效性.load(Ordering::Relaxed) {
"True"
} else {
"False"
},
);
}
// 非笔:线段/扩展线段等,完整输出
let (, , , 贯穿伤) = crate::algorithm::segment::线段::分割序列(self, None);
let (特征_a, 特征_b, 特征_c) = crate::algorithm::segment::线段::特征序列状态(self);
let 特征_bool = |b: bool| -> &str { if b { "True" } else { "False" } };
let 前一缺口_str = match &*self.前一缺口.read().unwrap() {
Some(g) => format!("{}", g),
None => "None".to_string(),
};
let 前一结束位置_str = match &*self.前一结束位置.read().unwrap() {
Some(d) => format!("{}", d),
None => "None".to_string(),
};
// Format中枢序列 as Python-style list representations
let _str = format!(
"[{}]",
self._中枢序列
.read()
.unwrap()
.iter()
.map(|h| format!("{}", h))
.collect::<Vec<_>>()
.join(", ")
);
let _str = format!(
"[{}]",
self._中枢序列
.read()
.unwrap()
.iter()
.map(|h| format!("{}", h))
.collect::<Vec<_>>()
.join(", ")
);
let _str = format!(
"[{}]",
self._中枢序列
.read()
.unwrap()
.iter()
.map(|h| format!("{}", h))
.collect::<Vec<_>>()
.join(", ")
);
let _str = format!(
"[{}]",
.iter()
.map(|d| format!("{}", d))
.collect::<Vec<_>>()
.join(", ")
);
let _str = format!(
"[{}]",
.iter()
.map(|d| format!("{}", d))
.collect::<Vec<_>>()
.join(", ")
);
let _str = format!(
"[{}]",
.iter()
.map(|d| format!("{}", d))
.collect::<Vec<_>>()
.join(", ")
);
format!(
"{}, {}, {}, 文:({},{}), 武:({},{}), {}, {}, ({}, {}, {}), (前: {}, 后: {}, 三: {}, 伤: {}), 实: {}, 虚: {}, 合: {}, {}, {}, {}, {}",
self.标识.read().unwrap(),
self.序号.load(Ordering::Relaxed),
self.级别.load(Ordering::Relaxed),
self..时间戳(),
format_f64_g(self..分型特征值),
self..read().unwrap().时间戳(),
format_f64_g(self..read().unwrap().分型特征值),
if self.有效性.load(Ordering::Relaxed) {
"True"
} else {
"False"
},
self.基础序列.read().unwrap().len(),
特征_bool(特征_a),
特征_bool(特征_b),
特征_bool(特征_c),
_str,
_str,
_str,
match &贯穿伤 {
Some(d) => format!("{}", d),
None => "None".to_string(),
},
_str,
_str,
_str,
self.模式.read().unwrap(),
前一缺口_str,
前一结束位置_str,
if self.短路修正.load(Ordering::Relaxed) {
"True"
} else {
"False"
},
)
}
// ---- 关联函数(静态工厂方法) ----
/// 创建笔
pub fn 创建笔(: Arc<分型>, : Arc<分型>, 有效性: bool) -> Self {
Self::new(0, "笔".into(), , , 1, 有效性)
}
/// 创建线段
pub fn 创建线段(虚线序列: &[Arc<虚线>]) -> Self {
let 序列数量 = 虚线序列.len();
if 序列数量 < 3 {
panic!("创建线段 虚线序列 数量 {} < 3", 序列数量);
}
if 序列数量.is_multiple_of(2) {
panic!("创建线段 虚线序列 数量 {} 不是单数!", 序列数量);
}
let = Arc::clone(&虚线序列[0].);
let = Arc::clone(&*虚线序列[虚线序列.len() - 1]..read().unwrap());
assert!(
.结构 != .结构,
"创建线段: 文.结构 == 武.结构 文={}, 武={}",
,
);
let 标识: String = if *虚线序列[0].标识.read().unwrap() == "笔" {
"线段".into()
} else {
format!("线段<{}>", 虚线序列[0].标识.read().unwrap())
};
let 级别 = 虚线序列[0].级别.load(Ordering::Relaxed) + 1;
let = Self::new(0, 标识, , , 级别, true);
*.特征序列.write().unwrap() = vec![None, None, None];
*._中枢序列.write().unwrap() = Vec::new();
*._中枢序列.write().unwrap() = Vec::new();
*._中枢序列.write().unwrap() = Vec::new();
*.基础序列.write().unwrap() = 虚线序列.to_vec();
*.模式.write().unwrap() = "文武".into();
}
// ---- 买卖点模式匹配 ----
/// 缠K买卖点模式 — 根据模式字符串选择匹配方法
pub fn K买卖点模式(模式: &str, K: &缠论K线, 配置: &缠论配置) -> bool {
match 模式 {
"全量" => Self::买卖点全量匹配(K),
"任意" => Self::买卖点任意匹配(K),
"配置" => Self::买卖点配置匹配(K, 配置),
"相对" => Self::买卖点相对匹配(K),
_ => false,
}
}
/// 买卖点配置匹配 — 根据配置中的指标开关组合判断
pub fn 买卖点配置匹配(K: &缠论K线, 配置: &缠论配置) -> bool {
match (
配置.买卖点_指标匹配_MACD,
配置.买卖点_指标匹配_KDJ,
配置.买卖点_指标匹配_RSI,
) {
(true, true, true) => K.MACD柱子匹配() && K.KDJ匹配() && K.RSI匹配(),
(false, false, false) => false,
(true, false, true) => K.MACD柱子匹配() && K.RSI匹配(),
(false, true, false) => K.KDJ匹配(),
(true, false, false) => K.MACD柱子匹配(),
(false, true, true) => K.KDJ匹配() && K.RSI匹配(),
(false, false, true) => K.RSI匹配(),
(true, true, false) => K.MACD柱子匹配() && K.KDJ匹配(),
}
}
/// 买卖点任意匹配 — 任一指标匹配
pub fn 买卖点任意匹配(K: &缠论K线) -> bool {
K.MACD柱子匹配() || K.KDJ匹配() || K.RSI匹配()
}
/// 买卖点全量匹配 — 全部指标匹配
pub fn 买卖点全量匹配(K: &缠论K线) -> bool {
K.MACD柱子匹配() && K.KDJ匹配() && K.RSI匹配()
}
/// 买卖点相对匹配 — 至少两个指标匹配
pub fn 买卖点相对匹配(K: &缠论K线) -> bool {
let 混沌槽 = [K.MACD柱子匹配(), K.KDJ匹配(), K.RSI匹配()];
混沌槽.iter().filter(|&&x| x).count() >= 2
}
// ---- MACD柱子均值计算 ----
/// 计算MACD柱子均值 — 虚线范围内所有MACD柱的绝对值均值
pub fn 计算MACD柱子均值(K序列: &[Arc<K线>], 实线: &虚线) -> f64 {
let K线序列 = K线::截取rc(
K序列,
&实线...标的K线.read().unwrap(),
&实线..read().unwrap()..标的K线.read().unwrap(),
);
if K线序列.is_empty() {
return 0.0;
}
let : f64 = K线序列
.iter()
.filter_map(|k| k.指标.read().unwrap().macd_cloned())
.map(|m| m.MACD柱.abs())
.sum();
/ K线序列.len() as f64
}
/// 计算MACD柱子均值_阴 — 负柱的绝对值均值
/// 对齐说明:Python 无数据时返回 Falsebool),Rust 返回 None,调用方处理等价
pub fn 计算MACD柱子均值_阴(K序列: &[Arc<K线>], 实线: &虚线) -> Option<f64> {
let K线序列 = K线::截取rc(
K序列,
&实线...标的K线.read().unwrap(),
&实线..read().unwrap()..标的K线.read().unwrap(),
);
let : Vec<f64> = K线序列
.iter()
.filter_map(|k| k.指标.read().unwrap().macd_cloned())
.filter(|m| m.MACD柱 < 0.0)
.map(|m| m.MACD柱.abs())
.collect();
if .is_empty() {
None
} else {
Some(.iter().sum::<f64>() / .len() as f64)
}
}
/// 计算MACD柱子均值_阳 — 正柱的绝对值均值
/// 对齐说明:Python 无数据时返回 Falsebool),Rust 返回 None,调用方处理等价
pub fn 计算MACD柱子均值_阳(K序列: &[Arc<K线>], 实线: &虚线) -> Option<f64> {
let K线序列 = K线::截取rc(
K序列,
&实线...标的K线.read().unwrap(),
&实线..read().unwrap()..标的K线.read().unwrap(),
);
let : Vec<f64> = K线序列
.iter()
.filter_map(|k| k.指标.read().unwrap().macd_cloned())
.filter(|m| m.MACD柱 > 0.0)
.map(|m| m.MACD柱.abs())
.collect();
if .is_empty() {
None
} else {
Some(.iter().sum::<f64>() / .len() as f64)
}
}
// ---- 武之MACD比较 ----
/// 武之全量MACD均值 — 武端MACD柱是否小于均值(背驰)
pub fn 武之全量MACD均值(K序列: &[Arc<K线>], 实线: &虚线) -> bool {
let _ref = 实线..read().unwrap();
let = _ref..标的K线.read().unwrap();
let _MACD = match .指标.read().unwrap().macd() {
Some(m) => m.MACD柱.abs(),
None => return false,
};
_MACD < Self::计算MACD柱子均值(K序列, 实线)
}
/// 武之MACD均值 — 按方向选择阴/阳均值比对
pub fn 武之MACD均值(K序列: &[Arc<K线>], 实线: &虚线) -> bool {
if 实线.方向() == 相对方向::向上 {
Self::武之MACD均值_阳(K序列, 实线)
} else {
Self::武之MACD均值_阴(K序列, 实线)
}
}
/// 武之MACD均值_阴 — 武端负柱是否小于阴均值
pub fn 武之MACD均值_阴(K序列: &[Arc<K线>], 实线: &虚线) -> bool {
let _ref = 实线..read().unwrap();
let = _ref..标的K线.read().unwrap();
let _MACD = match .指标.read().unwrap().macd() {
Some(m) => m.MACD柱.abs(),
None => return false,
};
match Self::计算MACD柱子均值_阴(K序列, 实线) {
Some(均值) => _MACD < 均值.abs(),
None => false,
}
}
/// 武之MACD均值_阳 — 武端正柱是否小于阳均值
pub fn 武之MACD均值_阳(K序列: &[Arc<K线>], 实线: &虚线) -> bool {
let _ref = 实线..read().unwrap();
let = _ref..标的K线.read().unwrap();
let _MACD = match .指标.read().unwrap().macd() {
Some(m) => m.MACD柱.abs(),
None => return false,
};
match Self::计算MACD柱子均值_阳(K序列, 实线) {
Some(均值) => _MACD < 均值,
None => false,
}
}
/// 武之MACD极值 — 武端MACD柱是否为区间极值
pub fn 武之MACD极值(K序列: &[Arc<K线>], 实线: &虚线) -> bool {
let _ref = 实线..read().unwrap();
let = _ref..标的K线.read().unwrap();
let _MACD = match .指标.read().unwrap().macd() {
Some(m) => m.MACD柱,
None => return false,
};
let K线序列 = K线::截取rc(
K序列,
&实线...标的K线.read().unwrap(),
&实线..read().unwrap()..标的K线.read().unwrap(),
);
let 所有柱子: Vec<f64> = K线序列
.iter()
.filter_map(|k| k.指标.read().unwrap().macd_cloned())
.map(|m| m.MACD柱)
.collect();
if 所有柱子.is_empty() {
return false;
}
if _MACD > 0.0 {
let 极值 = 所有柱子.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
极值 == _MACD
} else {
let 极值 = 所有柱子.iter().cloned().fold(f64::INFINITY, f64::min);
极值 == _MACD
}
}
// ---- MACD趋向背驰 ----
/// 计算K线序列MACD趋向背驰 — 分析 MACD柱/DIF/DEA 三项背驰信号
pub fn 计算K线序列MACD趋向背驰(
K序列: &[Arc<K线>], 方向: 相对方向
) -> [bool; 3] {
if K序列.is_empty() {
return [false, false, false];
}
let 最后 = &K序列[K序列.len() - 1];
if 方向 == 相对方向::向上 {
let 柱子序列: Vec<&Arc<K线>> = K序列
.iter()
.filter(|k| {
k.指标
.read()
.unwrap()
.macd()
.is_some_and(|m| m.MACD柱 > 0.0)
})
.collect();
if 柱子序列.is_empty() {
return [false, false, false];
}
let mut 结果 = [false; 3];
// MACD柱背驰
let 最高柱子 = 柱子序列
.iter()
.max_by(|a, b| {
a.指标
.read()
.unwrap()
.macd()
.unwrap()
.MACD柱
.partial_cmp(&b.指标.read().unwrap().macd().unwrap().MACD柱)
.unwrap_or(std::cmp::Ordering::Equal)
})
.unwrap();
let mut 柱对 = [Arc::clone(*最高柱子), Arc::clone(最后)];
柱对.sort_by_key(|k| k.时间戳);
let m0_g = 柱对[0].指标.read().unwrap();
let m1_g = 柱对[1].指标.read().unwrap();
if let (Some(m0), Some(m1)) = (m0_g.macd(), m1_g.macd())
&& m0.MACD柱 > m1.MACD柱
&& 柱对[0]. < 柱对[1].
{
结果[0] = true;
}
// DIF背驰 (no sort — Python compares peak vs last directly)
let 最高离差值 = 柱子序列
.iter()
.max_by(|a, b| {
let da = a
.指标
.read()
.unwrap()
.macd()
.and_then(|m| m.DIF)
.unwrap_or(0.0);
let db = b
.指标
.read()
.unwrap()
.macd()
.and_then(|m| m.DIF)
.unwrap_or(0.0);
da.partial_cmp(&db).unwrap_or(std::cmp::Ordering::Equal)
})
.unwrap();
let m0_g = 最高离差值.指标.read().unwrap();
let m1_g = 最后.指标.read().unwrap();
if let (Some(m0), Some(m1)) = (m0_g.macd(), m1_g.macd()) {
let dif0 = m0.DIF.unwrap_or(0.0);
let dif1 = m1.DIF.unwrap_or(0.0);
if dif0 > dif1 && 最高离差值. < 最后. {
结果[1] = true;
}
}
// DEA背驰 (no sort — Python compares peak vs last directly)
let 最高信号线 = 柱子序列
.iter()
.max_by(|a, b| {
let da = a
.指标
.read()
.unwrap()
.macd()
.and_then(|m| m.DEA)
.unwrap_or(0.0);
let db = b
.指标
.read()
.unwrap()
.macd()
.and_then(|m| m.DEA)
.unwrap_or(0.0);
da.partial_cmp(&db).unwrap_or(std::cmp::Ordering::Equal)
})
.unwrap();
let m0_g = 最高信号线.指标.read().unwrap();
let m1_g = 最后.指标.read().unwrap();
if let (Some(m0), Some(m1)) = (m0_g.macd(), m1_g.macd()) {
let dea0 = m0.DEA.unwrap_or(0.0);
let dea1 = m1.DEA.unwrap_or(0.0);
if dea0 > dea1 && 最高信号线. < 最后. {
结果[2] = true;
}
}
结果
} else {
let 柱子序列: Vec<&Arc<K线>> = K序列
.iter()
.filter(|k| {
k.指标
.read()
.unwrap()
.macd()
.is_some_and(|m| m.MACD柱 < 0.0)
})
.collect();
if 柱子序列.is_empty() {
return [false, false, false];
}
let mut 结果 = [false; 3];
// MACD柱背驰 (负向: absolute value comparison)
let 最高柱子 = 柱子序列
.iter()
.max_by(|a, b| {
a.指标
.read()
.unwrap()
.macd()
.unwrap()
.MACD柱
.abs()
.partial_cmp(&b.指标.read().unwrap().macd().unwrap().MACD柱.abs())
.unwrap_or(std::cmp::Ordering::Equal)
})
.unwrap();
let mut 柱对 = [Arc::clone(*最高柱子), Arc::clone(最后)];
柱对.sort_by_key(|k| k.时间戳);
let m0_g = 柱对[0].指标.read().unwrap();
let m1_g = 柱对[1].指标.read().unwrap();
if let (Some(m0), Some(m1)) = (m0_g.macd(), m1_g.macd())
&& m0.MACD柱 < m1.MACD柱
&& 柱对[0]. > 柱对[1].
{
结果[0] = true;
}
// DIF背驰 (no sort — Python compares peak vs last directly)
let 最高离差值 = 柱子序列
.iter()
.max_by(|a, b| {
let da = a
.指标
.read()
.unwrap()
.macd()
.and_then(|m| m.DIF)
.unwrap_or(0.0)
.abs();
let db = b
.指标
.read()
.unwrap()
.macd()
.and_then(|m| m.DIF)
.unwrap_or(0.0)
.abs();
da.partial_cmp(&db).unwrap_or(std::cmp::Ordering::Equal)
})
.unwrap();
let m0_g = 最高离差值.指标.read().unwrap();
let m1_g = 最后.指标.read().unwrap();
if let (Some(m0), Some(m1)) = (m0_g.macd(), m1_g.macd()) {
let dif0 = m0.DIF.unwrap_or(0.0);
let dif1 = m1.DIF.unwrap_or(0.0);
if dif0 < dif1 && 最高离差值. > 最后. {
结果[1] = true;
}
}
// DEA背驰 (no sort — Python compares peak vs last directly)
let 最高信号线 = 柱子序列
.iter()
.max_by(|a, b| {
let da = a
.指标
.read()
.unwrap()
.macd()
.and_then(|m| m.DEA)
.unwrap_or(0.0)
.abs();
let db = b
.指标
.read()
.unwrap()
.macd()
.and_then(|m| m.DEA)
.unwrap_or(0.0)
.abs();
da.partial_cmp(&db).unwrap_or(std::cmp::Ordering::Equal)
})
.unwrap();
let m0_g = 最高信号线.指标.read().unwrap();
let m1_g = 最后.指标.read().unwrap();
if let (Some(m0), Some(m1)) = (m0_g.macd(), m1_g.macd()) {
let dea0 = m0.DEA.unwrap_or(0.0);
let dea1 = m1.DEA.unwrap_or(0.0);
if dea0 < dea1 && 最高信号线. > 最后. {
结果[2] = true;
}
}
结果
}
}
// ---- MACD柱子分段 ----
/// 计算MACD柱子分段 — 按正负号将MACD柱子分段
/// 对齐说明:Python 丢弃末尾分段的末段残留(疑似 bug),Rust 通过 if !当前段.is_empty() 正确追加
pub fn 计算MACD柱子分段(k线序列: &[Arc<K线>]) -> Vec<Vec<f64>> {
if k线序列.is_empty() {
return Vec::new();
}
let 符号 = |x: f64| -> &str { if x > 0.0 { "正" } else { "负" } };
let _MACD = match k线序列[0].指标.read().unwrap().macd() {
Some(m) => m.MACD柱,
None => return Vec::new(),
};
let mut 当前符号 = 符号(_MACD);
let mut 当前段 = vec![_MACD];
let mut 结果 = Vec::new();
for k线 in &k线序列[1..] {
let macd = match k线.指标.read().unwrap().macd() {
Some(m) => m.MACD柱,
None => continue,
};
let 新符号 = 符号(macd);
if 新符号 == 当前符号 {
当前段.push(macd);
} else {
结果.push(std::mem::take(&mut 当前段));
当前段.push(macd);
当前符号 = 新符号;
}
}
if !当前段.is_empty() {
结果.push(当前段);
}
结果
}
// ---- 密集区域按间隔 ----
/// 密集区域按间隔 — 找出交叉标记中的密集区域
pub fn 密集区域按间隔(
交叉标记: &[i32],
最大间隔: usize,
最少交叉数: usize,
) -> Vec<(usize, usize, usize)> {
let 交叉索引: Vec<usize> = (0..交叉标记.len()).filter(|&i| 交叉标记[i] != 0).collect();
if 交叉索引.is_empty() {
return Vec::new();
}
let mut 密集区 = Vec::new();
let mut 当前块起始 = 交叉索引[0];
let mut 当前块交叉数 = 1;
for i in 1..交叉索引.len() {
let prev = 交叉索引[i - 1];
let curr = 交叉索引[i];
if curr - prev <= 最大间隔 {
当前块交叉数 += 1;
} else {
if 当前块交叉数 >= 最少交叉数 {
密集区.push((当前块起始, prev, 当前块交叉数));
}
当前块起始 = curr;
当前块交叉数 = 1;
}
}
if 当前块交叉数 >= 最少交叉数 {
密集区.push((当前块起始, 交叉索引[交叉索引.len() - 1], 当前块交叉数));
}
密集区
}
// ---- 统计MACD行为 ----
/// 统计MACD行为 — 分析DIF/DEA穿零轴和金叉死叉
pub fn 统计MACD行为(
K序列: &[Arc<K线>],
最大间隔: usize,
最少交叉数: usize,
) -> MACD行为统计 {
let mut dif_up = 0;
let mut dif_down = 0;
let mut dea_up = 0;
let mut dea_down = 0;
for i in 1..K序列.len() {
let pre_guard = K序列[i - 1].指标.read().unwrap();
let cur_guard = K序列[i].指标.read().unwrap();
let pre = pre_guard.macd();
let cur = cur_guard.macd();
if pre.is_none() || cur.is_none() {
continue;
}
let (pre_dif, cur_dif) = (pre.unwrap().DIF, cur.unwrap().DIF);
let (pre_dea, cur_dea) = (pre.unwrap().DEA, cur.unwrap().DEA);
if let (Some(pd), Some(cd)) = (pre_dif, cur_dif) {
if pd < 0.0 && cd >= 0.0 {
dif_up += 1;
}
if pd > 0.0 && cd <= 0.0 {
dif_down += 1;
}
}
if let (Some(pd), Some(cd)) = (pre_dea, cur_dea) {
if pd < 0.0 && cd >= 0.0 {
dea_up += 1;
}
if pd > 0.0 && cd <= 0.0 {
dea_down += 1;
}
}
}
let mut golden = 0;
let mut death = 0;
let mut 交叉标记 = vec![0i32];
for i in 1..K序列.len() {
let pre_guard = K序列[i - 1].指标.read().unwrap();
let cur_guard = K序列[i].指标.read().unwrap();
let pre = pre_guard.macd();
let cur = cur_guard.macd();
if pre.is_none() || cur.is_none() {
交叉标记.push(0);
continue;
}
let pre_dif = pre.unwrap().DIF;
let pre_dea = pre.unwrap().DEA;
let cur_dif = cur.unwrap().DIF;
let cur_dea = cur.unwrap().DEA;
if let (Some(pd), Some(cd), Some(pe), Some(ce)) = (pre_dif, cur_dif, pre_dea, cur_dea) {
if pd <= pe && cd > ce {
golden += 1;
交叉标记.push(1);
} else if pd >= pe && cd < ce {
death += 1;
交叉标记.push(-1);
} else {
交叉标记.push(0);
}
} else {
交叉标记.push(0);
}
}
let 密集交叉区域 = Self::密集区域按间隔(&交叉标记, 最大间隔, 最少交叉数);
MACD行为统计 {
DIF上穿0: dif_up,
DIF下穿0: dif_down,
DEA上穿0: dea_up,
DEA下穿0: dea_down,
金叉次数: golden,
死叉次数: death,
密集交叉区域,
}
}
// ---- 买卖意义 ----
/// 买卖意义 — 核心买卖点判断逻辑
///
/// 返回 (是否有意义, 原因字符串)
pub fn 买卖意义(
实线: &虚线, 观察员: &crate::business::observer::观察者
) -> (bool, String) {
// LRU 缓存查找
let key = (
std::ptr::from_ref(实线) as usize,
std::ptr::from_ref(观察员) as usize,
);
{
let mut cache = 买卖意义缓存.lock().unwrap();
if let Some(val) = cached::Cached::cache_get(&mut *cache, &key) {
return val.clone();
}
}
let result = Self::_买卖意义_计算(实线, 观察员);
{
let mut cache = 买卖意义缓存.lock().unwrap();
cached::Cached::cache_set(&mut *cache, key, result.clone());
}
result
}
/// 买卖意义 实际计算(无缓存)
fn _买卖意义_计算(
实线: &虚线,
观察员: &crate::business::observer::观察者,
) -> (bool, String) {
let K序列 = &观察员.普通K线序列;
let 配置 = &观察员.配置;
if *实线.标识.read().unwrap() != "笔"
&& *实线.标识.read().unwrap() != "线段"
&& *实线.标识.read().unwrap() != "线段<线段>"
{
return (false, "标识不在范围内".into());
}
// KDJ指标完整性检查
let _ref = 实线..read().unwrap();
let = _ref..标的K线.read().unwrap();
match .指标.read().unwrap().kdj() {
Some(kdj) if kdj.K.is_some() && kdj.D.is_some() && kdj.J.is_some() => {}
_ => return (false, "KDJ指标不完整".into()),
}
let 意义 =
Self::K买卖点模式(&配置.买卖点_指标模式, &实线..read().unwrap()., 配置);
let 结果 = false;
let 背驰过: Vec<Arc<缠论K线>> = if *实线.标识.read().unwrap() == "笔" {
crate::algorithm::bi::::是否背驰过(实线, 观察员)
} else {
crate::algorithm::segment::线段::是否背驰过(实线, 观察员)
};
if 意义 {
if *实线.标识.read().unwrap() == "笔" {
if Self::武之MACD均值(K序列, 实线) {
return (true, "武之MACD均值".into());
}
if Self::武之MACD极值(K序列, 实线) && !背驰过.is_empty() {
return (true, "背驰过且极值".into());
} else if 实线..read().unwrap().MACD柱子分型匹配() {
return (
true,
format!(
"背驰过:{},极值:{},柱子分型匹配",
背驰过.len(),
if Self::武之MACD极值(K序列, 实线) {
"True"
} else {
"False"
}
),
);
}
}
if *实线.标识.read().unwrap() != "笔"
&& crate::algorithm::segment::线段::判断线段内部是否背驰(实线, 观察员)
{
return (true, "线段内部背驰".into());
}
}
if !结果
&& 意义
&& 实线..read().unwrap()..MACD柱子匹配()
&& Self::武之MACD极值(K序列, 实线)
&& 背驰过.len() > 2
{
return (true, "没结果, 极值, 柱子分型匹配, 背驰过大于2次".into());
}
(结果, "".into())
}
/// 结构化相等校验 — 递归校验所有子结构(分型/缺口/缠K/中枢/线段特征/虚线),返回 (是否相等, 差异描述)
pub fn 相等(&self, other: &Self, 浮点容差: f64) -> (bool, String) {
if *self.标识.read().unwrap() != *other.标识.read().unwrap() {
return (
false,
format!(
"虚线: [标识] 不等 A={},B={}",
self.标识.read().unwrap(),
other.标识.read().unwrap()
),
);
}
if self.序号.load(Ordering::Relaxed) != other.序号.load(Ordering::Relaxed) {
return (
false,
format!(
"虚线: [序号] 不等 A={},B={}",
self.序号.load(Ordering::Relaxed),
other.序号.load(Ordering::Relaxed)
),
);
}
if self.级别.load(Ordering::Relaxed) != other.级别.load(Ordering::Relaxed) {
return (
false,
format!(
"虚线: [级别] 不等 A={},B={}",
self.级别.load(Ordering::Relaxed),
other.级别.load(Ordering::Relaxed)
),
);
}
// 文
{
let (eq, msg) = self..相等(&other., 浮点容差);
if !eq {
return (false, format!("虚线: [文]分型异常 >> {msg}"));
}
}
// 武
{
let (eq, msg) = self
.
.read()
.unwrap()
.相等(&other..read().unwrap(), 浮点容差);
if !eq {
return (false, format!("虚线: [武]分型异常 >> {msg}"));
}
}
if self.有效性.load(Ordering::Relaxed) != other.有效性.load(Ordering::Relaxed) {
return (
false,
format!(
"虚线: [有效性] 不等 A={},B={}",
self.有效性.load(Ordering::Relaxed),
other.有效性.load(Ordering::Relaxed)
),
);
}
// 基础序列
{
let a = self.基础序列.read().unwrap();
let b = other.基础序列.read().unwrap();
if a.len() != b.len() {
return (
false,
format!("虚线: [基础序列] 长度不一致 A={},B={}", a.len(), b.len()),
);
}
for (idx, (x, y)) in a.iter().zip(b.iter()).enumerate() {
let (eq, msg) = x.相等(y, 浮点容差);
if !eq {
return (false, format!("虚线: 基础序列[{idx}]虚线异常 >> {msg}"));
}
}
}
// 特征序列
{
let a = self.特征序列.read().unwrap();
let b = other.特征序列.read().unwrap();
if a.len() != b.len() {
return (
false,
format!("虚线: [特征序列] 长度不一致 A={},B={}", a.len(), b.len()),
);
}
for (idx, (x, y)) in a.iter().zip(b.iter()).enumerate() {
match (x, y) {
(None, None) => {}
(Some(xx), Some(yy)) => {
let (eq, msg) = xx.相等(yy, 浮点容差);
if !eq {
return (false, format!("虚线: 特征序列[{idx}]线段特征异常 >> {msg}"));
}
}
_ => return (false, format!("虚线: 特征序列[{idx}]空值不一致")),
}
}
}
// 中枢序列
let 检查中枢列表 =
|: &str, a: &[Arc<中枢>], b: &[Arc<中枢>], 容差: f64| -> Result<(), String> {
if a.len() != b.len() {
return Err(format!(
"虚线: [{名}] 长度不一致 A={},B={}",
a.len(),
b.len()
));
}
for (idx, (x, y)) in a.iter().zip(b.iter()).enumerate() {
let (eq, msg) = x.相等(y, 容差);
if !eq {
return Err(format!("虚线: {名}[{idx}]中枢异常 >> {msg}"));
}
}
Ok(())
};
检查中枢列表(
"实_中枢序列",
&self._中枢序列.read().unwrap(),
&other._中枢序列.read().unwrap(),
浮点容差,
)
.map_err(|e| (false, e))
.ok();
检查中枢列表(
"虚_中枢序列",
&self._中枢序列.read().unwrap(),
&other._中枢序列.read().unwrap(),
浮点容差,
)
.map_err(|e| (false, e))
.ok();
检查中枢列表(
"合_中枢序列",
&self._中枢序列.read().unwrap(),
&other._中枢序列.read().unwrap(),
浮点容差,
)
.map_err(|e| (false, e))
.ok();
// 确认K线
match (
&*self.确认K线.read().unwrap(),
&*other.确认K线.read().unwrap(),
) {
(None, None) => {}
(Some(a), Some(b)) => {
let (eq, msg) = a.相等(b, 浮点容差);
if !eq {
return (false, format!("虚线: [确认K线]缠论K线异常 >> {msg}"));
}
}
(a, b) => {
return (
false,
format!(
"虚线: [确认K线]空值不一致 A={},B={}",
a.is_some(),
b.is_some()
),
);
}
}
// 模式
if *self.模式.read().unwrap() != *other.模式.read().unwrap() {
return (
false,
format!(
"虚线: [模式] 不等 A={},B={}",
self.模式.read().unwrap(),
other.模式.read().unwrap()
),
);
}
// _特征序列_显示
if self._特征序列_显示.load(Ordering::Relaxed)
!= other._特征序列_显示.load(Ordering::Relaxed)
{
return (false, "虚线: [_特征序列_显示] 不等".to_string());
}
// 前一缺口
match (
&*self.前一缺口.read().unwrap(),
&*other.前一缺口.read().unwrap(),
) {
(None, None) => {}
(Some(a), Some(b)) => {
let (eq, msg) = a.相等(b, 浮点容差);
if !eq {
return (false, format!("虚线: [前一缺口]缺口异常 >> {msg}"));
}
}
(a, b) => {
return (
false,
format!(
"虚线: [前一缺口]空值不一致 A={},B={}",
a.is_some(),
b.is_some()
),
);
}
}
// 前一结束位置
match (
&*self.前一结束位置.read().unwrap(),
&*other.前一结束位置.read().unwrap(),
) {
(None, None) => {}
(Some(a), Some(b)) => {
let (eq, msg) = a.相等(b, 浮点容差);
if !eq {
return (false, format!("虚线: [前一结束位置]虚线异常 >> {msg}"));
}
}
(a, b) => {
return (
false,
format!(
"虚线: [前一结束位置]空值不一致 A={},B={}",
a.is_some(),
b.is_some()
),
);
}
}
// 短路修正
if self.短路修正.load(Ordering::Relaxed) != other.短路修正.load(Ordering::Relaxed) {
return (false, "虚线: [短路修正] 不等".to_string());
}
(true, "虚线: 全量字段嵌套校验一致".into())
}
}
impl std::fmt::Display for 虚线 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if *self.标识.read().unwrap() == "笔" {
write!(
f,
"笔({}, {}, {}, {}, 周期: {}, 数量: {})",
self.序号.load(Ordering::Relaxed),
self.方向(),
self.,
self..read().unwrap(),
self...周期,
self..read().unwrap()..序号.load(Ordering::Relaxed)
- self...序号.load(Ordering::Relaxed)
+ 1
)
} else {
let 四象 = crate::algorithm::segment::线段::四象(self);
let 缺口 = crate::algorithm::segment::线段::获取缺口(self);
let 缺口_str = match 缺口 {
Some(g) => format!("{}", g),
None => "None".to_string(),
};
let 确认K线_str = match &*self.确认K线.read().unwrap() {
Some(k) => format!("{}", k),
None => "None".to_string(),
};
write!(
f,
"{}<{}, {}, {}, {}, {}, 数量: {}, 缺口: {}, {}>",
self.标识.read().unwrap(),
self.序号.load(Ordering::Relaxed),
四象,
self.方向(),
self.,
self..read().unwrap(),
self.基础序列.read().unwrap().len(),
缺口_str,
确认K线_str,
)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::kline::bar::K线;
use crate::kline::chan_kline::缠论K线;
use crate::types::分型结构;
/// 辅助:创建一根最小化的原始K线
fn 辅助_创建K线(时间戳: i64, : f64, : f64, : f64, : f64) -> K线 {
K线 {
时间戳,
,
,
开盘价: ,
收盘价: ,
..Default::default()
}
}
/// 辅助:创建一根缠论K线
fn 辅助_创建缠K(
时间戳: i64,
: f64,
: f64,
方向: 相对方向,
结构: Option<分型结构>,
序号: i64,
) -> Arc<缠论K线> {
let K = Arc::new(辅助_创建K线(时间戳, , , , ));
let K = 缠论K线::创建缠K(时间戳, , , 方向, 结构, 序号, K, None);
Arc::new(K)
}
/// 辅助:创建顶分型
fn 辅助_创建顶分型(时间戳: i64, : f64, : f64, 序号: i64) -> Arc<分型> {
let = 辅助_创建缠K(
时间戳 - 2,
- 2.0,
- 2.0,
相对方向::向上,
Some(分型结构::),
序号 - 2,
);
let = 辅助_创建缠K(时间戳, , , 相对方向::向上, Some(分型结构::), 序号);
let = 辅助_创建缠K(
时间戳 + 2,
- 1.0,
- 1.0,
相对方向::向下,
Some(分型结构::),
序号 + 2,
);
Arc::new(分型::new(Some(), , Some()))
}
/// 辅助:创建底分型
fn 辅助_创建底分型(时间戳: i64, : f64, : f64, 序号: i64) -> Arc<分型> {
let = 辅助_创建缠K(
时间戳 - 2,
+ 2.0,
+ 2.0,
相对方向::向下,
Some(分型结构::),
序号 - 2,
);
let = 缠论K线::创建缠K(
时间戳,
,
,
相对方向::向下,
Some(分型结构::),
序号,
Arc::new(辅助_创建K线(时间戳, , , , )),
None,
);
.分型特征值.set();
let = Arc::new();
let = 辅助_创建缠K(
时间戳 + 2,
+ 1.0,
+ 1.0,
相对方向::向上,
Some(分型结构::),
序号 + 2,
);
Arc::new(分型::new(Some(), , Some()))
}
// ============================================================
// Cell 字段读写测试
// ============================================================
#[test]
fn test_Cell字段读写一致性() {
let = 辅助_创建顶分型(100, 50.0, 40.0, 5);
let = 辅助_创建底分型(200, 30.0, 20.0, 10);
let = 虚线::创建笔(, , true);
assert_eq!(.序号.load(Ordering::Relaxed), 0);
assert!(.有效性.load(Ordering::Relaxed));
assert!(!.短路修正.load(Ordering::Relaxed));
assert!(.前一缺口.read().unwrap().is_none());
// 修改 Cell 字段
.序号.store(42, Ordering::Relaxed);
.有效性.store(false, Ordering::Relaxed);
.短路修正.store(true, Ordering::Relaxed);
*.前一缺口.write().unwrap() = Some(缺口::new(200.0, 100.0));
assert_eq!(.序号.load(Ordering::Relaxed), 42);
assert!(!.有效性.load(Ordering::Relaxed));
assert!(.短路修正.load(Ordering::Relaxed));
let qk = .前一缺口.read().unwrap().unwrap();
assert!((qk. - 200.0).abs() < 0.01);
assert!((qk. - 100.0).abs() < 0.01);
}
// ============================================================
// RefCell 字段读写测试
// ============================================================
#[test]
fn test_RefCell字段读写一致性() {
let = 辅助_创建顶分型(100, 50.0, 40.0, 5);
let = 辅助_创建底分型(200, 30.0, 20.0, 10);
let = 虚线::创建笔(, , true);
// 标识
assert_eq!(*.标识.read().unwrap(), "笔");
*.标识.write().unwrap() = "测试标识".into();
assert_eq!(*.标识.read().unwrap(), "测试标识");
// 模式
assert_eq!(*.模式.read().unwrap(), "文武");
*.模式.write().unwrap() = "全量".into();
assert_eq!(*.模式.read().unwrap(), "全量");
// 基础序列
assert!(.基础序列.read().unwrap().is_empty());
let 另一底 = 辅助_创建底分型(300, 20.0, 10.0, 15);
let 2 = 虚线::创建笔(Arc::clone(&*..read().unwrap()), 另一底, true);
.基础序列.write().unwrap().push(Arc::new(2));
assert_eq!(.基础序列.read().unwrap().len(), 1);
// 武 - Replace with new 分型
let 新底 = 辅助_创建底分型(400, 15.0, 5.0, 20);
let 新底_ptr = Arc::as_ptr(&新底);
*..write().unwrap() = Arc::clone(&新底);
assert_eq!(Arc::as_ptr(&*..read().unwrap()), 新底_ptr);
}
// ============================================================
// Clone 后 Rc 指针身份一致
// ============================================================
#[test]
fn test_虚线Clone后文Rc指针一致() {
let = 辅助_创建顶分型(100, 50.0, 40.0, 5);
let = 辅助_创建底分型(200, 30.0, 20.0, 10);
let = 虚线::创建笔(Arc::clone(&), Arc::clone(&), true);
let 克隆笔 = .clone();
// 文 Rc 指针应一致
assert_eq!(Arc::as_ptr(&.), Arc::as_ptr(&));
assert_eq!(Arc::as_ptr(&克隆笔.), Arc::as_ptr(&.));
// 武 Rc 指针应一致
assert_eq!(Arc::as_ptr(&*..read().unwrap()), Arc::as_ptr(&));
assert_eq!(
Arc::as_ptr(&*克隆笔..read().unwrap()),
Arc::as_ptr(&*..read().unwrap())
);
}
#[test]
fn test_虚线Clone是深拷贝Cell值而非共享() {
let = 辅助_创建顶分型(100, 50.0, 40.0, 5);
let = 辅助_创建底分型(200, 30.0, 20.0, 10);
let = 虚线::创建笔(, , true);
.序号.store(10, Ordering::Relaxed);
let 克隆笔 = .clone();
// Clone 后序号应独立(deep copy for Cell
克隆笔.序号.store(99, Ordering::Relaxed);
assert_eq!(.序号.load(Ordering::Relaxed), 10);
assert_eq!(克隆笔.序号.load(Ordering::Relaxed), 99);
// Rc 指针仍应一致(文/武 共享)
assert_eq!(Arc::as_ptr(&.), Arc::as_ptr(&克隆笔.));
}
// ============================================================
// 多 Rc 共享下 Cell/RefCell 修改可见性
// ============================================================
#[test]
fn test_多Rc共享下Cell修改对所有引用可见() {
let = 辅助_创建顶分型(100, 50.0, 40.0, 5);
let = 辅助_创建底分型(200, 30.0, 20.0, 10);
let _rc1 = Arc::new(虚线::创建笔(, , true));
let _rc2 = Arc::clone(&_rc1);
// 通过 rc1 修改 Cell
_rc1.序号.store(77, Ordering::Relaxed);
// rc2 应能看到
assert_eq!(_rc2.序号.load(Ordering::Relaxed), 77);
// 通过 rc1 修改 RefCell
*_rc1.模式.write().unwrap() = "配置".into();
assert_eq!(*_rc2.模式.read().unwrap(), "配置");
// 通过 rc1 修改 武
let 新底 = 辅助_创建底分型(400, 15.0, 5.0, 20);
let 新底_ptr = Arc::as_ptr(&新底);
*_rc1..write().unwrap() = Arc::clone(&新底);
assert_eq!(Arc::as_ptr(&*_rc2..read().unwrap()), 新底_ptr);
}
// ============================================================
// 获取_武 递归正确性
// ============================================================
#[test]
fn test_获取武_笔级别直接返回武() {
let = 辅助_创建顶分型(100, 50.0, 40.0, 5);
let = 辅助_创建底分型(200, 30.0, 20.0, 10);
let = 虚线::创建笔(Arc::clone(&), Arc::clone(&), true);
let wu = .获取_武();
assert_eq!(Arc::as_ptr(&*..read().unwrap()), Arc::as_ptr(&wu));
assert_eq!(Arc::as_ptr(&wu), Arc::as_ptr(&));
}
#[test]
fn test_获取武_线段级别递归到底层笔() {
let 1 = 辅助_创建顶分型(100, 50.0, 40.0, 5);
let 1 = 辅助_创建底分型(200, 30.0, 20.0, 10);
let 2 = 辅助_创建顶分型(300, 55.0, 45.0, 15);
let 2 = 辅助_创建底分型(400, 25.0, 15.0, 20);
let 1 = Arc::new(虚线::创建笔(Arc::clone(&1), Arc::clone(&1), true));
let 2 = Arc::new(虚线::创建笔(Arc::clone(&1), Arc::clone(&2), true));
let 3 = Arc::new(虚线::创建笔(Arc::clone(&2), Arc::clone(&2), true));
let = 虚线::创建线段(&[Arc::clone(&1), Arc::clone(&2), Arc::clone(&3)]);
// 线段的 获取_武 应返回底层最后一笔的武(底2)
let wu = .获取_武();
assert_eq!(Arc::as_ptr(&wu), Arc::as_ptr(&2));
// 笔1 的 获取_武 应返回底1
let wu1 = 1.获取_武();
assert_eq!(Arc::as_ptr(&wu1), Arc::as_ptr(&1));
}
// ============================================================
// 虚线 字段原子性 - 修改不影响文(不可变字段)
// ============================================================
#[test]
fn test_修改武不影响文Rc指针() {
let = 辅助_创建顶分型(100, 50.0, 40.0, 5);
let 1 = 辅助_创建底分型(200, 30.0, 20.0, 10);
let 2 = 辅助_创建底分型(300, 25.0, 15.0, 15);
let = 虚线::创建笔(Arc::clone(&), Arc::clone(&1), true);
let _ptr_before = Arc::as_ptr(&.);
// 修改武
*..write().unwrap() = Arc::clone(&2);
// 文指针不变
assert_eq!(Arc::as_ptr(&.), _ptr_before);
// 但方向变了(因为武从底1变成底2)
let 新武耗时 = ..read().unwrap().时间戳();
assert_eq!(新武耗时, 300);
}
}