第二版
This commit is contained in:
+167
-63
@@ -12,7 +12,11 @@ pub struct 笔;
|
||||
|
||||
impl 笔 {
|
||||
/// 获取可成笔的缠K数量(考虑弱化模式)
|
||||
pub fn 获取缠K数量(缠K序列: &[Rc<缠论K线>], 笔序列: &[Rc<虚线>], 配置: &缠论配置) -> usize {
|
||||
pub fn 获取缠K数量(
|
||||
缠K序列: &[Rc<缠论K线>],
|
||||
笔序列: &[Rc<虚线>],
|
||||
配置: &缠论配置,
|
||||
) -> usize {
|
||||
let 实际数量 = 缠K序列.len();
|
||||
if 实际数量 >= 配置.笔内元素数量 as usize {
|
||||
return 实际数量;
|
||||
@@ -31,13 +35,19 @@ impl 笔 {
|
||||
|
||||
if !笔序列.is_empty() {
|
||||
// Try both high and low points (Python: 根据缠K找笔(笔序列, 实际高点) or 根据缠K找笔(笔序列, 实际低点))
|
||||
let 筆 = 实际高点.as_ref()
|
||||
let 筆 = 实际高点
|
||||
.as_ref()
|
||||
.and_then(|h| Self::根据缠K找笔(笔序列, h, 1))
|
||||
.or_else(|| 实际低点.as_ref().and_then(|l| Self::根据缠K找笔(笔序列, l, 1)));
|
||||
.or_else(|| {
|
||||
实际低点
|
||||
.as_ref()
|
||||
.and_then(|l| Self::根据缠K找笔(笔序列, l, 1))
|
||||
});
|
||||
|
||||
if let Some(ref 筆) = 筆 {
|
||||
if let (Some(ref 高_k), Some(ref 低_k)) = (&实际高点, &实际低点) {
|
||||
let 原始数量 = 1 + (低_k.标的K线.序号 - 高_k.标的K线.序号).unsigned_abs() as usize;
|
||||
let 原始数量 =
|
||||
1 + (低_k.标的K线.序号 - 高_k.标的K线.序号).unsigned_abs() as usize;
|
||||
// 向上笔
|
||||
if 筆.方向().是否向上() && 低_k.低 < 筆.低() {
|
||||
if 原始数量 >= 配置.笔弱化_原始数量 as usize {
|
||||
@@ -62,15 +72,25 @@ impl 笔 {
|
||||
if 缠K序列.len() < 2 {
|
||||
return 缠K序列.first().cloned();
|
||||
}
|
||||
let max_高 = 缠K序列.iter().map(|k| k.高).fold(f64::NEG_INFINITY, f64::max);
|
||||
let max_高 = 缠K序列
|
||||
.iter()
|
||||
.map(|k| k.高)
|
||||
.fold(f64::NEG_INFINITY, f64::max);
|
||||
// 排除最高值
|
||||
let filtered: Vec<&Rc<缠论K线>> = 缠K序列.iter().filter(|k| k.高 != max_高).collect();
|
||||
if filtered.is_empty() {
|
||||
return 缠K序列.first().cloned();
|
||||
}
|
||||
// 筛选次高值
|
||||
let second_高 = filtered.iter().map(|k| k.高).fold(f64::NEG_INFINITY, f64::max);
|
||||
let mut candidates: Vec<&Rc<缠论K线>> = filtered.iter().filter(|k| k.高 == second_高).copied().collect();
|
||||
let second_高 = filtered
|
||||
.iter()
|
||||
.map(|k| k.高)
|
||||
.fold(f64::NEG_INFINITY, f64::max);
|
||||
let mut candidates: Vec<&Rc<缠论K线>> = filtered
|
||||
.iter()
|
||||
.filter(|k| k.高 == second_高)
|
||||
.copied()
|
||||
.collect();
|
||||
// 按时间戳排序
|
||||
candidates.sort_by(|a, b| a.时间戳.cmp(&b.时间戳));
|
||||
if 取舍 {
|
||||
@@ -93,7 +113,11 @@ impl 笔 {
|
||||
}
|
||||
// 筛选次低值
|
||||
let second_低 = filtered.iter().map(|k| k.低).fold(f64::INFINITY, f64::min);
|
||||
let mut candidates: Vec<&Rc<缠论K线>> = filtered.iter().filter(|k| k.低 == second_低).copied().collect();
|
||||
let mut candidates: Vec<&Rc<缠论K线>> = filtered
|
||||
.iter()
|
||||
.filter(|k| k.低 == second_低)
|
||||
.copied()
|
||||
.collect();
|
||||
// 按时间戳排序
|
||||
candidates.sort_by(|a, b| a.时间戳.cmp(&b.时间戳));
|
||||
if 取舍 {
|
||||
@@ -108,7 +132,10 @@ impl 笔 {
|
||||
if 缠K序列.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let max_高 = 缠K序列.iter().map(|k| k.高).fold(f64::NEG_INFINITY, f64::max);
|
||||
let max_高 = 缠K序列
|
||||
.iter()
|
||||
.map(|k| k.高)
|
||||
.fold(f64::NEG_INFINITY, f64::max);
|
||||
let mut candidates: Vec<&Rc<缠论K线>> = 缠K序列.iter().filter(|k| k.高 == max_高).collect();
|
||||
if candidates.is_empty() {
|
||||
return Some(Rc::clone(&缠K序列[0]));
|
||||
@@ -142,30 +169,84 @@ impl 笔 {
|
||||
}
|
||||
|
||||
/// 判断笔的相对关系是否合理
|
||||
pub fn 相对关系(筆: &虚线, _配置: &缠论配置) -> bool {
|
||||
let 文 = &筆.文;
|
||||
let 武 = &筆.武;
|
||||
pub fn 相对关系(筆: &虚线, 配置: &缠论配置) -> bool {
|
||||
let 文分型 = &筆.文;
|
||||
let 武分型 = &筆.武;
|
||||
|
||||
// 向上笔:文(底)低 → 武(顶)高
|
||||
if 筆.方向().是否向上() {
|
||||
return 武.分型特征值 > 文.分型特征值;
|
||||
let 相对关系 = if 配置.笔内起始分型包含整笔 {
|
||||
let 文中_rc = Rc::clone(&文分型.中);
|
||||
let 武中_rc = Rc::clone(&武分型.中);
|
||||
let 文_元素: [Option<&Rc<缠论K线>>; 3] =
|
||||
[文分型.左.as_ref(), Some(&文中_rc), 文分型.右.as_ref()];
|
||||
let 有效序列: Vec<&Rc<缠论K线>> = 文_元素.iter().filter_map(|x| *x).collect();
|
||||
let 文高 = 有效序列
|
||||
.iter()
|
||||
.map(|k| k.高)
|
||||
.fold(f64::NEG_INFINITY, f64::max);
|
||||
let 文低 = 有效序列.iter().map(|k| k.低).fold(f64::INFINITY, f64::min);
|
||||
|
||||
let 武_右: Option<&Rc<缠论K线>> = if 配置.笔内起始分型包含整笔_包括右 {
|
||||
武分型.右.as_ref()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let 武_元素: [Option<&Rc<缠论K线>>; 3] = [武分型.左.as_ref(), Some(&武中_rc), 武_右];
|
||||
let 有效序列: Vec<&Rc<缠论K线>> = 武_元素.iter().filter_map(|x| *x).collect();
|
||||
let 武高 = 有效序列
|
||||
.iter()
|
||||
.map(|k| k.高)
|
||||
.fold(f64::NEG_INFINITY, f64::max);
|
||||
let 武低 = 有效序列.iter().map(|k| k.低).fold(f64::INFINITY, f64::min);
|
||||
|
||||
crate::types::相对方向::分析(文高, 文低, 武高, 武低)
|
||||
} else {
|
||||
let 相对关系 = crate::types::相对方向::分析(
|
||||
文分型.中.高,
|
||||
文分型.中.低,
|
||||
武分型.中.高,
|
||||
武分型.中.低,
|
||||
);
|
||||
if 配置.笔内原始K线包含整笔 {
|
||||
let 文标的 = &文分型.中.标的K线;
|
||||
let 武标的 = &武分型.中.标的K线;
|
||||
if crate::types::相对方向::分析(文标的.高, 文标的.低, 武标的.高, 武标的.低)
|
||||
.是否包含()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
相对关系
|
||||
};
|
||||
|
||||
if 筆.方向() == crate::types::相对方向::向下 {
|
||||
return 相对关系.是否向下();
|
||||
}
|
||||
// 向下笔:文(顶)高 → 武(底)低
|
||||
武.分型特征值 < 文.分型特征值
|
||||
相对关系.是否向上()
|
||||
}
|
||||
|
||||
/// 以文会友 — 根据起点分型找笔
|
||||
pub fn 以文会友(笔序列: &[Rc<虚线>], 文: &Rc<分型>) -> Option<Rc<虚线>> {
|
||||
笔序列.iter().find(|b| Rc::as_ptr(&b.文) == Rc::as_ptr(文)).cloned()
|
||||
笔序列
|
||||
.iter()
|
||||
.find(|b| Rc::as_ptr(&b.文) == Rc::as_ptr(文))
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/// 以武会友 — 根据终点分型找笔
|
||||
pub fn 以武会友(笔序列: &[Rc<虚线>], 武: &Rc<分型>) -> Option<Rc<虚线>> {
|
||||
笔序列.iter().find(|b| Rc::as_ptr(&b.武) == Rc::as_ptr(武)).cloned()
|
||||
笔序列
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|b| Rc::as_ptr(&b.武) == Rc::as_ptr(武))
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/// 根据缠K找对应的笔
|
||||
pub fn 根据缠K找笔(笔序列: &[Rc<虚线>], 缠K: &Rc<缠论K线>, 偏移: i64) -> Option<Rc<虚线>> {
|
||||
pub fn 根据缠K找笔(
|
||||
笔序列: &[Rc<虚线>],
|
||||
缠K: &Rc<缠论K线>,
|
||||
偏移: i64,
|
||||
) -> Option<Rc<虚线>> {
|
||||
// Python iterates in reverse: for 筆 in 笔序列[::-1]
|
||||
for b in 笔序列.iter().rev() {
|
||||
// Python: 筆.文.中.序号 - 偏移 <= 缠K.序号 <= 筆.武.中.序号
|
||||
@@ -282,7 +363,8 @@ impl 笔 {
|
||||
let 之前分型 = Rc::clone(分型序列.last().unwrap());
|
||||
|
||||
// Python line 2338: 时序检查 — skip out-of-order fractals
|
||||
if 之前分型.时间戳 > 当前分型.时间戳 && 之前分型.中.序号 - 当前分型.中.序号 > 1 {
|
||||
if 之前分型.时间戳 > 当前分型.时间戳 && 之前分型.中.序号 - 当前分型.中.序号 > 1
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -310,8 +392,12 @@ impl 笔 {
|
||||
|
||||
// Python line 2350: 分型结构相反 → 可能成笔
|
||||
if 之前分型.结构 != 当前分型.结构 {
|
||||
let 文_idx = 缠K序列.iter().position(|k| Rc::as_ptr(k) == Rc::as_ptr(&之前分型.中));
|
||||
let 武_idx = 缠K序列.iter().position(|k| Rc::as_ptr(k) == Rc::as_ptr(&当前分型.中));
|
||||
let 文_idx = 缠K序列
|
||||
.iter()
|
||||
.position(|k| Rc::as_ptr(k) == Rc::as_ptr(&之前分型.中));
|
||||
let 武_idx = 缠K序列
|
||||
.iter()
|
||||
.position(|k| Rc::as_ptr(k) == Rc::as_ptr(&当前分型.中));
|
||||
|
||||
if let (Some(文_idx), Some(武_idx)) = (文_idx, 武_idx) {
|
||||
let 基础序列 = &缠K序列[文_idx..=武_idx];
|
||||
@@ -339,7 +425,9 @@ impl 笔 {
|
||||
|
||||
// Python line 2369-2372: 武将
|
||||
let 武将 = match 当前分型.结构 {
|
||||
分型结构::底 => Self::实际低点(基础序列, 配置.笔内相同终点取舍),
|
||||
分型结构::底 => {
|
||||
Self::实际低点(基础序列, 配置.笔内相同终点取舍)
|
||||
}
|
||||
_ => Self::实际高点(基础序列, 配置.笔内相同终点取舍),
|
||||
};
|
||||
|
||||
@@ -377,7 +465,9 @@ impl 笔 {
|
||||
} else {
|
||||
// Python line 2388-2390: 元素不足 → 右元素扩展
|
||||
if let Some(ref 右) = 当前分型.右 {
|
||||
if let Some(临时分型) = 分型::从缠K序列中获取分型(缠K序列, 右) {
|
||||
if let Some(临时分型) =
|
||||
分型::从缠K序列中获取分型(缠K序列, 右)
|
||||
{
|
||||
栈.push(栈项::分型(Rc::new(临时分型), 递归层次 + 1));
|
||||
continue;
|
||||
}
|
||||
@@ -398,8 +488,7 @@ impl 笔 {
|
||||
let 被替换分型 = Rc::clone(&之前分型);
|
||||
Self::弹出旧笔(分型序列, 笔序列);
|
||||
|
||||
if let Some(k线序列) =
|
||||
缠论K线::截取(缠K序列, &被替换分型.中, &当前分型.中)
|
||||
if let Some(k线序列) = 缠论K线::截取(缠K序列, &被替换分型.中, &当前分型.中)
|
||||
{
|
||||
let 武将 = match 被替换分型.结构 {
|
||||
分型结构::顶 => {
|
||||
@@ -490,7 +579,8 @@ impl 笔 {
|
||||
|
||||
// Python line 2337-2341: 时序检查
|
||||
let 之前分型 = Rc::clone(分型序列.last().unwrap());
|
||||
if 之前分型.时间戳 > 当前分型.时间戳 && 之前分型.中.序号 - 当前分型.中.序号 > 1 {
|
||||
if 之前分型.时间戳 > 当前分型.时间戳 && 之前分型.中.序号 - 当前分型.中.序号 > 1
|
||||
{
|
||||
println!("时序错误-{}, {}, {}", 递归层次, 之前分型, 当前分型);
|
||||
return 递归层次;
|
||||
}
|
||||
@@ -509,7 +599,13 @@ impl 笔 {
|
||||
if 破位 {
|
||||
Self::弹出旧笔(分型序列, 笔序列);
|
||||
return Self::分析递归(
|
||||
当前分型, 分型序列, 笔序列, 缠K序列, _普K序列, 递归层次 + 1, 配置,
|
||||
当前分型,
|
||||
分型序列,
|
||||
笔序列,
|
||||
缠K序列,
|
||||
_普K序列,
|
||||
递归层次 + 1,
|
||||
配置,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -518,7 +614,8 @@ impl 笔 {
|
||||
// Python line 2350: 分型结构相反 → 可能成笔
|
||||
let 之前分型 = Rc::clone(分型序列.last().unwrap());
|
||||
if 之前分型.结构 != 当前分型.结构 {
|
||||
if let Some(基础序列) = 缠论K线::截取(缠K序列, &之前分型.中, &当前分型.中) {
|
||||
if let Some(基础序列) = 缠论K线::截取(缠K序列, &之前分型.中, &当前分型.中)
|
||||
{
|
||||
let 当前笔 = Rc::new(虚线::创建笔(
|
||||
Rc::clone(&之前分型),
|
||||
Rc::clone(&当前分型),
|
||||
@@ -549,7 +646,11 @@ impl 笔 {
|
||||
配置,
|
||||
);
|
||||
return Self::分析递归(
|
||||
当前分型, 分型序列, 笔序列, 缠K序列, _普K序列,
|
||||
当前分型,
|
||||
分型序列,
|
||||
笔序列,
|
||||
缠K序列,
|
||||
_普K序列,
|
||||
递归层次 + 1,
|
||||
配置,
|
||||
);
|
||||
@@ -559,9 +660,7 @@ impl 笔 {
|
||||
|
||||
// Python line 2369-2375: 武将 and笔形成
|
||||
let 武将 = match 当前分型.结构 {
|
||||
分型结构::底 => {
|
||||
Self::实际低点(&基础序列, 配置.笔内相同终点取舍)
|
||||
}
|
||||
分型结构::底 => Self::实际低点(&基础序列, 配置.笔内相同终点取舍),
|
||||
_ => Self::实际高点(&基础序列, 配置.笔内相同终点取舍),
|
||||
};
|
||||
|
||||
@@ -578,9 +677,7 @@ impl 笔 {
|
||||
// Python line 2378-2385: 笔次级成笔
|
||||
if 配置.笔次级成笔 {
|
||||
let 武将 = match 当前分型.结构 {
|
||||
分型结构::底 => {
|
||||
Self::次低(&基础序列, 配置.笔内相同终点取舍)
|
||||
}
|
||||
分型结构::底 => Self::次低(&基础序列, 配置.笔内相同终点取舍),
|
||||
_ => Self::次高(&基础序列, 配置.笔内相同终点取舍),
|
||||
};
|
||||
if let Some(ref 武将_k) = 武将 {
|
||||
@@ -595,8 +692,7 @@ impl 笔 {
|
||||
} else {
|
||||
// Python line 2388-2390: 元素不足 → 右元素扩展
|
||||
if let Some(ref 右) = 当前分型.右 {
|
||||
if let Some(临时分型) =
|
||||
分型::从缠K序列中获取分型(缠K序列, 右)
|
||||
if let Some(临时分型) = 分型::从缠K序列中获取分型(缠K序列, 右)
|
||||
{
|
||||
return Self::分析递归(
|
||||
Rc::new(临时分型),
|
||||
@@ -626,13 +722,10 @@ impl 笔 {
|
||||
let 被替换分型 = Rc::clone(&之前分型);
|
||||
Self::弹出旧笔(分型序列, 笔序列);
|
||||
|
||||
if let Some(k线序列) =
|
||||
缠论K线::截取(缠K序列, &被替换分型.中, &当前分型.中)
|
||||
if let Some(k线序列) = 缠论K线::截取(缠K序列, &被替换分型.中, &当前分型.中)
|
||||
{
|
||||
let 武将 = match 被替换分型.结构 {
|
||||
分型结构::顶 => {
|
||||
Self::实际低点(&k线序列, 配置.笔内相同终点取舍)
|
||||
}
|
||||
分型结构::顶 => Self::实际低点(&k线序列, 配置.笔内相同终点取舍),
|
||||
_ => Self::实际高点(&k线序列, 配置.笔内相同终点取舍),
|
||||
};
|
||||
|
||||
@@ -667,10 +760,11 @@ impl 笔 {
|
||||
|| ck.分型 == Some(分型结构::顶)
|
||||
{
|
||||
if let Some(错过分型) =
|
||||
分型::从缠K序列中获取分型(缠K序列, ck)
|
||||
分型::从缠K序列中获取分型(
|
||||
缠K序列, ck,
|
||||
)
|
||||
{
|
||||
let 错过分型_rc =
|
||||
Rc::new(错过分型);
|
||||
let 错过分型_rc = Rc::new(错过分型);
|
||||
递归层次 = Self::分析递归(
|
||||
Rc::clone(&错过分型_rc),
|
||||
分型序列,
|
||||
@@ -687,7 +781,11 @@ impl 笔 {
|
||||
}
|
||||
|
||||
return Self::分析递归(
|
||||
当前分型, 分型序列, 笔序列, 缠K序列, _普K序列,
|
||||
当前分型,
|
||||
分型序列,
|
||||
笔序列,
|
||||
缠K序列,
|
||||
_普K序列,
|
||||
递归层次 + 1,
|
||||
配置,
|
||||
);
|
||||
@@ -700,7 +798,13 @@ impl 笔 {
|
||||
分型::向序列中添加(分型序列, 当前分型);
|
||||
} else {
|
||||
return Self::分析递归(
|
||||
当前分型, 分型序列, 笔序列, 缠K序列, _普K序列, 递归层次 + 1, 配置,
|
||||
当前分型,
|
||||
分型序列,
|
||||
笔序列,
|
||||
缠K序列,
|
||||
_普K序列,
|
||||
递归层次 + 1,
|
||||
配置,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -714,20 +818,17 @@ impl 笔 {
|
||||
分型序列: &mut Vec<Rc<分型>>,
|
||||
笔序列: &mut Vec<Rc<虚线>>,
|
||||
新分型: Rc<分型>,
|
||||
新笔: Rc<虚线>,
|
||||
mut 新笔: Rc<虚线>,
|
||||
) {
|
||||
分型序列.push(新分型);
|
||||
let 序号 = if 笔序列.is_empty() {
|
||||
0
|
||||
} else {
|
||||
笔序列.last().unwrap().序号 + 1
|
||||
};
|
||||
let mut 虚线笔 = (*新笔).clone();
|
||||
虚线笔.序号 = 序号;
|
||||
if 虚线笔.武.左.is_none() && 虚线笔.武.右.is_none() {
|
||||
虚线笔.有效性 = false;
|
||||
if !笔序列.is_empty() {
|
||||
let seg = Rc::make_mut(&mut 新笔);
|
||||
seg.序号 = 笔序列.last().unwrap().序号 + 1;
|
||||
if seg.武.左.is_none() && seg.武.右.is_none() {
|
||||
seg.有效性 = false;
|
||||
}
|
||||
}
|
||||
笔序列.push(Rc::new(虚线笔));
|
||||
笔序列.push(新笔);
|
||||
}
|
||||
|
||||
/// 自检 — 验证笔的有效性(文为实际高/低点,武为实际低/高点)
|
||||
@@ -735,13 +836,15 @@ impl 笔 {
|
||||
let 笔序列 = &观察员.笔序列;
|
||||
let 配置 = &观察员.配置;
|
||||
let 基础序列 = 筆.获取缠K序列(&观察员.缠论K线序列);
|
||||
if Self::获取缠K数量(&基础序列, 笔序列, 配置) >= 配置.笔内元素数量 as usize {
|
||||
if Self::获取缠K数量(&基础序列, 笔序列, 配置) >= 配置.笔内元素数量 as usize
|
||||
{
|
||||
if 筆.方向() == 相对方向::向下 {
|
||||
if let (Some(实际高), Some(实际低)) = (
|
||||
Self::实际高点(&基础序列, false),
|
||||
Self::实际低点(&基础序列, 配置.笔内相同终点取舍),
|
||||
) {
|
||||
if Rc::ptr_eq(&筆.文.中, &实际高) && Rc::ptr_eq(&筆.武.中, &实际低) {
|
||||
if Rc::ptr_eq(&筆.文.中, &实际高) && Rc::ptr_eq(&筆.武.中, &实际低)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -751,7 +854,8 @@ impl 笔 {
|
||||
Self::实际低点(&基础序列, false),
|
||||
Self::实际高点(&基础序列, 配置.笔内相同终点取舍),
|
||||
) {
|
||||
if Rc::ptr_eq(&筆.文.中, &实际低) && Rc::ptr_eq(&筆.武.中, &实际高) {
|
||||
if Rc::ptr_eq(&筆.文.中, &实际低) && Rc::ptr_eq(&筆.武.中, &实际高)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,86 +1,134 @@
|
||||
use crate::config::缠论配置;
|
||||
use crate::kline::bar::K线;
|
||||
use crate::structure::dash_line::虚线;
|
||||
use crate::types::相对方向;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// 背驰分析 — 判断进入段和离开段之间是否存在背驰
|
||||
pub struct 背驰分析;
|
||||
|
||||
impl 背驰分析 {
|
||||
/// MACD背驰 — 离开段MACD柱子面积小于进入段
|
||||
pub fn MACD背驰(进入段: &虚线, 离开段: &虚线, K线序列: &[Rc<K线>], 方式: &str) -> bool {
|
||||
// 获取进入段和离开段对应的K线
|
||||
let 进入段_始 = 进入段.文.中.原始起始序号 as usize;
|
||||
let 进入段_终 = 进入段.武.中.原始结束序号 as usize;
|
||||
let 离开段_始 = 离开段.文.中.原始起始序号 as usize;
|
||||
let 离开段_终 = 离开段.武.中.原始结束序号 as usize;
|
||||
/// MACD背驰 — MACD柱状线面积背驰
|
||||
/// 方式: "总"=阳+|阴|总面积, 其他=按进入段方向选阳或阴
|
||||
pub fn MACD背驰(
|
||||
进入段: &虚线, 离开段: &虚线, K线序列: &[Rc<K线>], 方式: &str
|
||||
) -> bool {
|
||||
let 进入MACD =
|
||||
Self::_获取MACD面积(K线序列, &进入段.文.中.标的K线, &进入段.武.中.标的K线);
|
||||
let 离开MACD =
|
||||
Self::_获取MACD面积(K线序列, &离开段.文.中.标的K线, &离开段.武.中.标的K线);
|
||||
|
||||
if 进入段_终 >= K线序列.len() || 离开段_终 >= K线序列.len() {
|
||||
return false;
|
||||
}
|
||||
// 计算面积(绝对值求和)
|
||||
let 进入面积 = if 方式 == "总" {
|
||||
进入MACD.总().abs()
|
||||
} else if 进入段.方向() == 相对方向::向上 {
|
||||
进入MACD.阳.abs()
|
||||
} else {
|
||||
进入MACD.阴.abs()
|
||||
};
|
||||
let 离开面积 = if 方式 == "总" {
|
||||
离开MACD.总().abs()
|
||||
} else if 进入段.方向() == 相对方向::向上 {
|
||||
离开MACD.阳.abs()
|
||||
} else {
|
||||
离开MACD.阴.abs()
|
||||
};
|
||||
|
||||
let 进入MACD = Self::柱子面积(&K线序列[进入段_始..=进入段_终], 方式);
|
||||
let 离开MACD = Self::柱子面积(&K线序列[离开段_始..=离开段_终], 方式);
|
||||
|
||||
// 背驰条件: 离开段面积绝对值小于进入段
|
||||
let 进入总面积 = 进入MACD.阳 + 进入MACD.阴.abs();
|
||||
let 离开总面积 = 离开MACD.阳 + 离开MACD.阴.abs();
|
||||
|
||||
if 进入总面积 < f64::EPSILON {
|
||||
return false;
|
||||
}
|
||||
离开总面积 < 进入总面积
|
||||
离开面积 < 进入面积
|
||||
}
|
||||
|
||||
/// 斜率背驰 — 离开段斜率小于进入段
|
||||
/// 斜率背驰 — 价格斜率背驰
|
||||
pub fn 斜率背驰(进入段: &虚线, 离开段: &虚线) -> bool {
|
||||
let 进入斜率 = (进入段.武.分型特征值 - 进入段.文.分型特征值).abs()
|
||||
/ (进入段.武.时间戳 - 进入段.文.时间戳).abs() as f64;
|
||||
let 离开斜率 = (离开段.武.分型特征值 - 离开段.文.分型特征值).abs()
|
||||
/ (离开段.武.时间戳 - 离开段.文.时间戳).abs() as f64;
|
||||
let dx = (进入段.武.时间戳 - 进入段.文.时间戳) as f64;
|
||||
let dy = 进入段.武.分型特征值 - 进入段.文.分型特征值;
|
||||
let 进入斜率 = dy / dx;
|
||||
|
||||
离开斜率 < 进入斜率
|
||||
let dx = (离开段.武.时间戳 - 离开段.文.时间戳) as f64;
|
||||
let dy = 离开段.武.分型特征值 - 离开段.文.分型特征值;
|
||||
let 离开斜率 = dy / dx;
|
||||
|
||||
if 进入段.方向() == 相对方向::向上 {
|
||||
离开段.高() > 进入段.高() && 离开斜率.abs() < 进入斜率.abs()
|
||||
} else {
|
||||
离开段.低() < 进入段.低() && 离开斜率.abs() < 进入斜率.abs()
|
||||
}
|
||||
}
|
||||
|
||||
/// 测度背驰 — 价格测度的背驰判断
|
||||
/// 测度背驰 — 价格时间测度背驰
|
||||
pub fn 测度背驰(进入段: &虚线, 离开段: &虚线) -> bool {
|
||||
let 进入幅度 = (进入段.武.分型特征值 - 进入段.文.分型特征值).abs();
|
||||
let 离开幅度 = (离开段.武.分型特征值 - 离开段.文.分型特征值).abs();
|
||||
离开幅度 < 进入幅度
|
||||
let dx = (进入段.武.时间戳 - 进入段.文.时间戳) as f64;
|
||||
let dy = 进入段.武.分型特征值 - 进入段.文.分型特征值;
|
||||
let 进入测度 = (dx * dx + dy * dy).sqrt();
|
||||
|
||||
let dx = (离开段.武.时间戳 - 离开段.文.时间戳) as f64;
|
||||
let dy = 离开段.武.分型特征值 - 离开段.文.分型特征值;
|
||||
let 离开测度 = (dx * dx + dy * dy).sqrt();
|
||||
|
||||
if 进入段.方向() == 相对方向::向上 {
|
||||
离开段.高() > 进入段.高() && 离开测度.abs() < 进入测度.abs()
|
||||
} else {
|
||||
离开段.低() < 进入段.低() && 离开测度.abs() < 进入测度.abs()
|
||||
}
|
||||
}
|
||||
|
||||
/// 全量背驰 — MACD + 斜率 + 测度 三者全满足
|
||||
pub fn 全量背驰(进入段: &虚线, 离开段: &虚线, 普K序列: &[Rc<K线>]) -> bool {
|
||||
Self::MACD背驰(进入段, 离开段, 普K序列, "总")
|
||||
&& Self::斜率背驰(进入段, 离开段)
|
||||
&& Self::测度背驰(进入段, 离开段)
|
||||
&& Self::斜率背驰(进入段, 离开段)
|
||||
}
|
||||
|
||||
/// 任意背驰 — 任一条件满足即可
|
||||
pub fn 任意背驰(进入段: &虚线, 离开段: &虚线, 普K序列: &[Rc<K线>]) -> bool {
|
||||
Self::MACD背驰(进入段, 离开段, 普K序列, "总")
|
||||
|| Self::斜率背驰(进入段, 离开段)
|
||||
|| Self::测度背驰(进入段, 离开段)
|
||||
|| Self::斜率背驰(进入段, 离开段)
|
||||
}
|
||||
|
||||
/// 配置背驰 — 根据配置选择判断方式
|
||||
pub fn 配置背驰(进入段: &虚线, 离开段: &虚线, 普K序列: &[Rc<K线>], 配置: &缠论配置) -> bool {
|
||||
let mut result = true;
|
||||
if 配置.线段内部背驰_MACD {
|
||||
result = result && Self::MACD背驰(进入段, 离开段, 普K序列, "总");
|
||||
pub fn 配置背驰(
|
||||
进入段: &虚线,
|
||||
离开段: &虚线,
|
||||
普K序列: &[Rc<K线>],
|
||||
配置: &缠论配置,
|
||||
) -> bool {
|
||||
match (
|
||||
配置.线段内部背驰_MACD,
|
||||
配置.线段内部背驰_测度,
|
||||
配置.线段内部背驰_斜率,
|
||||
) {
|
||||
(true, true, true) => {
|
||||
Self::MACD背驰(进入段, 离开段, 普K序列, "总")
|
||||
&& Self::测度背驰(进入段, 离开段)
|
||||
&& Self::斜率背驰(进入段, 离开段)
|
||||
}
|
||||
(false, false, false) => false,
|
||||
|
||||
(true, false, true) => {
|
||||
Self::MACD背驰(进入段, 离开段, 普K序列, "总") && Self::斜率背驰(进入段, 离开段)
|
||||
}
|
||||
(false, true, false) => Self::测度背驰(进入段, 离开段),
|
||||
|
||||
(true, false, false) => Self::MACD背驰(进入段, 离开段, 普K序列, "总"),
|
||||
(false, true, true) => {
|
||||
Self::测度背驰(进入段, 离开段) && Self::斜率背驰(进入段, 离开段)
|
||||
}
|
||||
|
||||
(false, false, true) => Self::斜率背驰(进入段, 离开段),
|
||||
(true, true, false) => {
|
||||
Self::MACD背驰(进入段, 离开段, 普K序列, "总") && Self::测度背驰(进入段, 离开段)
|
||||
}
|
||||
}
|
||||
if 配置.线段内部背驰_斜率 {
|
||||
result = result && Self::斜率背驰(进入段, 离开段);
|
||||
}
|
||||
if 配置.线段内部背驰_测度 {
|
||||
result = result && Self::测度背驰(进入段, 离开段);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// 任选背驰
|
||||
/// 任选背驰 — 至少两个条件满足(多数投票)
|
||||
pub fn 任选背驰(进入段: &虚线, 离开段: &虚线, 普K序列: &[Rc<K线>]) -> bool {
|
||||
Self::任意背驰(进入段, 离开段, 普K序列)
|
||||
let 混沌槽 = [
|
||||
Self::MACD背驰(进入段, 离开段, 普K序列, "总"),
|
||||
Self::测度背驰(进入段, 离开段),
|
||||
Self::斜率背驰(进入段, 离开段),
|
||||
];
|
||||
混沌槽.iter().filter(|&&x| x).count() >= 2
|
||||
}
|
||||
|
||||
/// 背驰模式 — 根据模式字符串选择判断方式
|
||||
@@ -95,40 +143,34 @@ impl 背驰分析 {
|
||||
"全量" => Self::全量背驰(进入段, 离开段, 普K序列),
|
||||
"任意" => Self::任意背驰(进入段, 离开段, 普K序列),
|
||||
"配置" => Self::配置背驰(进入段, 离开段, 普K序列, 配置),
|
||||
"相对" => Self::测度背驰(进入段, 离开段),
|
||||
_ => Self::配置背驰(进入段, 离开段, 普K序列, 配置),
|
||||
"相对" => Self::任选背驰(进入段, 离开段, 普K序列),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 内部辅助 ----
|
||||
|
||||
fn 柱子面积(K线序列: &[Rc<K线>], 方式: &str) -> MACD面积 {
|
||||
fn _获取MACD面积(K线序列: &[Rc<K线>], 始: &Rc<K线>, 终: &Rc<K线>) -> MACD面积 {
|
||||
let 始_idx = K线序列.iter().position(|k| Rc::as_ptr(k) == Rc::as_ptr(始));
|
||||
let 终_idx = K线序列.iter().position(|k| Rc::as_ptr(k) == Rc::as_ptr(终));
|
||||
|
||||
let mut 阳 = 0.0f64;
|
||||
let mut 阴 = 0.0f64;
|
||||
for k in K线序列 {
|
||||
if let Some(ref macd) = k.macd {
|
||||
let hist = macd.MACD柱;
|
||||
match 方式 {
|
||||
"阳" => {
|
||||
if hist > 0.0 {
|
||||
阳 += hist
|
||||
}
|
||||
}
|
||||
"阴" => {
|
||||
if hist < 0.0 {
|
||||
阴 += hist
|
||||
}
|
||||
}
|
||||
"总" | _ => {
|
||||
if hist >= 0.0 {
|
||||
阳 += hist
|
||||
} else {
|
||||
阴 += hist
|
||||
}
|
||||
|
||||
if let (Some(始), Some(终)) = (始_idx, 终_idx) {
|
||||
let (始, 终) = if 始 <= 终 { (始, 终) } else { (终, 始) };
|
||||
for k in &K线序列[始..=终] {
|
||||
if let Some(ref macd) = k.macd {
|
||||
let hist = macd.MACD柱;
|
||||
if hist >= 0.0 {
|
||||
阳 += hist;
|
||||
} else {
|
||||
阴 += hist;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MACD面积 { 阳, 阴 }
|
||||
}
|
||||
}
|
||||
@@ -137,3 +179,9 @@ struct MACD面积 {
|
||||
阳: f64,
|
||||
阴: f64,
|
||||
}
|
||||
|
||||
impl MACD面积 {
|
||||
fn 总(&self) -> f64 {
|
||||
self.阳 + self.阴
|
||||
}
|
||||
}
|
||||
|
||||
+240
-63
@@ -35,7 +35,10 @@ impl 中枢 {
|
||||
pub fn 图表标题(&self) -> String {
|
||||
format!(
|
||||
"{}:{}:{}:{}",
|
||||
self.文().中.标识, self.文().中.周期, self.标识, self.序号
|
||||
self.文().中.标识,
|
||||
self.文().中.周期,
|
||||
self.标识,
|
||||
self.序号
|
||||
)
|
||||
}
|
||||
|
||||
@@ -91,6 +94,15 @@ impl 中枢 {
|
||||
self.第三买卖线 = Some(线);
|
||||
}
|
||||
|
||||
/// 获取序列 — 基础序列 + 第三买卖线(若有)
|
||||
pub fn 获取序列(&self) -> Vec<Rc<虚线>> {
|
||||
let mut 序列: Vec<Rc<虚线>> = self.基础序列.clone();
|
||||
if let Some(ref 三买) = self.第三买卖线 {
|
||||
序列.push(Rc::clone(三买));
|
||||
}
|
||||
序列
|
||||
}
|
||||
|
||||
pub fn 获取数据文本(&self) -> String {
|
||||
let 第三买卖线_str = match &self.第三买卖线 {
|
||||
Some(x) => format!("{}", x),
|
||||
@@ -115,24 +127,158 @@ impl 中枢 {
|
||||
}
|
||||
|
||||
/// 校验中枢合法性
|
||||
pub fn 校验合法性(&self, 虚线序列: &[Rc<虚线>], _中枢序列: &[Rc<中枢>]) -> bool {
|
||||
// 1. 检查基础序列中的元素是否仍在虚线序列中
|
||||
pub fn 校验合法性(&mut self, 序列: &[Rc<虚线>]) -> bool {
|
||||
let mut 有效序列 = self.基础序列.clone();
|
||||
let mut 无效序列: Vec<Rc<虚线>> = Vec::new();
|
||||
for 元素 in &self.基础序列 {
|
||||
if !虚线序列.iter().any(|x| Rc::as_ptr(x) == Rc::as_ptr(元素)) {
|
||||
if !序列.iter().any(|x| Rc::as_ptr(x) == Rc::as_ptr(元素)) {
|
||||
无效序列.push(Rc::clone(元素));
|
||||
}
|
||||
}
|
||||
|
||||
if !无效序列.is_empty() {
|
||||
let 无效 = &无效序列[0];
|
||||
if let Some(pos) = self
|
||||
.基础序列
|
||||
.iter()
|
||||
.position(|x| Rc::as_ptr(x) == Rc::as_ptr(无效))
|
||||
{
|
||||
有效序列 = self.基础序列[..pos].to_vec();
|
||||
}
|
||||
}
|
||||
|
||||
if 有效序列.len() < 3 {
|
||||
self.第三买卖线 = None;
|
||||
self.本级_第三买卖线 = None;
|
||||
return false;
|
||||
}
|
||||
|
||||
self.基础序列 = 有效序列;
|
||||
|
||||
let 中枢高 = self.高();
|
||||
let 中枢低 = self.低();
|
||||
有效序列 = Vec::new();
|
||||
for 元素 in &self.基础序列 {
|
||||
if crate::types::相对方向::分析(中枢高, 中枢低, 元素.高(), 元素.低()).是否缺口()
|
||||
{
|
||||
break;
|
||||
}
|
||||
有效序列.push(Rc::clone(元素));
|
||||
}
|
||||
self.基础序列 = 有效序列;
|
||||
|
||||
if self.基础序列.len() < 3 {
|
||||
return false;
|
||||
}
|
||||
|
||||
for i in 1..self.基础序列.len() {
|
||||
let 前 = &self.基础序列[i - 1];
|
||||
let 后 = &self.基础序列[i];
|
||||
if !前.之后是(后) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 2. 检查前三根重叠是否有效
|
||||
if self.基础序列.len() >= 3 {
|
||||
let 高 = self.高();
|
||||
let 低 = self.低();
|
||||
if 高 <= 低 {
|
||||
|
||||
if !crate::types::相对方向::分析(
|
||||
self.基础序列[0].高(),
|
||||
self.基础序列[0].低(),
|
||||
self.基础序列[2].高(),
|
||||
self.基础序列[2].低(),
|
||||
)
|
||||
.是否缺口()
|
||||
{
|
||||
let 重叠高 = self.高();
|
||||
let 重叠低 = self.低();
|
||||
if 重叠低 > 重叠高 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref 三买线) = self.第三买卖线.clone() {
|
||||
if 序列.iter().any(|x| Rc::as_ptr(x) == Rc::as_ptr(三买线)) {
|
||||
if !self.基础序列.last().unwrap().之后是(三买线) {
|
||||
self.第三买卖线 = None;
|
||||
} else if !crate::types::相对方向::分析(
|
||||
self.高(),
|
||||
self.低(),
|
||||
三买线.高(),
|
||||
三买线.低(),
|
||||
)
|
||||
.是否缺口()
|
||||
{
|
||||
self.添加虚线(Rc::clone(三买线));
|
||||
self.第三买卖线 = None;
|
||||
}
|
||||
} else {
|
||||
self.第三买卖线 = None;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// 完整性 — 详见教你炒股票43:有关背驰的补习课
|
||||
/// 不完整时下一个中枢大概率会与当前中枢发生扩展
|
||||
pub fn 完整性(&self, 虚实: &str) -> bool {
|
||||
if self.基础序列[0].标识 == "笔" {
|
||||
return self.第三买卖线.is_some();
|
||||
}
|
||||
|
||||
let 线段内部中枢 = if 虚实 == "合" {
|
||||
&self.基础序列.last().unwrap().合_中枢序列
|
||||
} else {
|
||||
&self.基础序列.last().unwrap().实_中枢序列
|
||||
};
|
||||
for 内部中枢 in 线段内部中枢 {
|
||||
if crate::types::相对方向::分析(
|
||||
self.高(),
|
||||
self.低(),
|
||||
内部中枢.高(),
|
||||
内部中枢.低(),
|
||||
)
|
||||
.是否缺口()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// 获取扩展中枢 — 当基础序列 >= 9 时生成扩展中枢
|
||||
pub fn 获取扩展中枢(
|
||||
&self, 扩展中枢: &mut Vec<Rc<中枢>>, 配置: &crate::config::缠论配置
|
||||
) {
|
||||
if self.基础序列.len() >= 9 {
|
||||
let mut 扩展线段: Vec<Rc<虚线>> = Vec::new();
|
||||
crate::algorithm::segment::线段::扩展分析(&self.基础序列, &mut 扩展线段, 配置);
|
||||
中枢::分析(
|
||||
&扩展线段,
|
||||
扩展中枢,
|
||||
false,
|
||||
&format!("{}_扩展中枢_", self.标识),
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 当前状态 — 详见教你炒股票49:利润率最大的操作模式
|
||||
/// 返回当前中枢最后一段所处的位置关系:中枢之中/中枢之上/中枢之下
|
||||
pub fn 当前状态(&self) -> &str {
|
||||
let 最后 = self.基础序列.last().unwrap();
|
||||
let 尾部_中 = if 最后.标识 == "笔" {
|
||||
&最后.武.中
|
||||
} else {
|
||||
&最后.基础序列.last().unwrap().武.中
|
||||
};
|
||||
let 关系 = crate::types::相对方向::分析(self.高(), self.低(), 尾部_中.高, 尾部_中.低);
|
||||
if 关系 == crate::types::相对方向::向上缺口 {
|
||||
"中枢之上"
|
||||
} else if 关系 == crate::types::相对方向::向下缺口 {
|
||||
"中枢之下"
|
||||
} else {
|
||||
"中枢之中"
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 关联函数 ----
|
||||
|
||||
/// 基础检查 — 三根虚线是否能形成中枢
|
||||
@@ -140,22 +286,27 @@ impl 中枢 {
|
||||
if !左.之后是(中) || !中.之后是(右) {
|
||||
return false;
|
||||
}
|
||||
let 高 = [左.高(), 中.高(), 右.高()]
|
||||
.iter()
|
||||
.copied()
|
||||
.min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
|
||||
.unwrap_or(0.0);
|
||||
let 低 = [左.低(), 中.低(), 右.低()]
|
||||
.iter()
|
||||
.copied()
|
||||
.max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
|
||||
.unwrap_or(0.0);
|
||||
高 > 低
|
||||
let 关系 = crate::types::相对方向::分析(左.高(), 左.低(), 右.高(), 右.低());
|
||||
matches!(
|
||||
关系,
|
||||
crate::types::相对方向::向下
|
||||
| crate::types::相对方向::向上
|
||||
| crate::types::相对方向::顺
|
||||
| crate::types::相对方向::逆
|
||||
| crate::types::相对方向::同
|
||||
)
|
||||
}
|
||||
|
||||
/// 创建中枢
|
||||
pub fn 创建(左: Rc<虚线>, 中: Rc<虚线>, 右: Rc<虚线>, 级别: i64, 标识: &str) -> Self {
|
||||
Self::new(0, format!("{}中枢<{}>", 标识, 中.标识), 级别, vec![左, 中, 右])
|
||||
pub fn 创建(
|
||||
左: Rc<虚线>, 中: Rc<虚线>, 右: Rc<虚线>, 级别: i64, 标识: &str
|
||||
) -> Self {
|
||||
Self::new(
|
||||
0,
|
||||
format!("{}中枢<{}>", 标识, 中.标识),
|
||||
级别,
|
||||
vec![左, 中, 右],
|
||||
)
|
||||
}
|
||||
|
||||
/// 从序列中获取中枢
|
||||
@@ -177,27 +328,32 @@ impl 中枢 {
|
||||
}
|
||||
|
||||
/// 向中枢序列尾部添加
|
||||
pub fn 向中枢序列尾部添加(中枢序列: &mut Vec<Rc<中枢>>, 待添加中枢: Rc<中枢>) {
|
||||
// Dedup
|
||||
pub fn 向中枢序列尾部添加(
|
||||
中枢序列: &mut Vec<Rc<中枢>>, mut 待添加中枢: Rc<中枢>
|
||||
) {
|
||||
if let Some(前一个) = 中枢序列.last() {
|
||||
if 前一个.高() == 待添加中枢.高() && 前一个.低() == 待添加中枢.低() {
|
||||
return;
|
||||
let 新 = Rc::make_mut(&mut 待添加中枢);
|
||||
新.序号 = 前一个.序号 + 1;
|
||||
// Python: assert seq[-1].获取序列()[-1].序号 <= new.获取序列()[-1].序号
|
||||
let 前_seq = 前一个.获取序列();
|
||||
let new_seq = 新.获取序列();
|
||||
if let (Some(前_last), Some(new_last)) = (前_seq.last(), new_seq.last()) {
|
||||
if 前_last.序号 > new_last.序号 {
|
||||
panic!(
|
||||
"向中枢序列尾部添加 序号错误 前last={} > new_last={}",
|
||||
前_last.序号, new_last.序号
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set序号: Python — if中枢序列: 待添加中枢.序号 = 中枢序列[-1].序号 + 1 else: stays 0
|
||||
if let Some(前一个) = 中枢序列.last() {
|
||||
let 新序号 = 前一个.序号 + 1;
|
||||
// Clone to mutate since Rc is immutable
|
||||
let mut cloned = (*待添加中枢).clone();
|
||||
cloned.序号 = 新序号;
|
||||
中枢序列.push(Rc::new(cloned));
|
||||
} else {
|
||||
中枢序列.push(待添加中枢);
|
||||
}
|
||||
中枢序列.push(待添加中枢);
|
||||
}
|
||||
|
||||
/// 从中枢序列尾部弹出
|
||||
pub fn 从中枢序列尾部弹出(中枢序列: &mut Vec<Rc<中枢>>, 待弹出: &Rc<中枢>) -> Option<Rc<中枢>> {
|
||||
pub fn 从中枢序列尾部弹出(
|
||||
中枢序列: &mut Vec<Rc<中枢>>,
|
||||
待弹出: &Rc<中枢>,
|
||||
) -> Option<Rc<中枢>> {
|
||||
if 中枢序列.last().map(|x| Rc::as_ptr(x)) == Some(Rc::as_ptr(待弹出)) {
|
||||
中枢序列.pop()
|
||||
} else {
|
||||
@@ -228,13 +384,17 @@ impl 中枢 {
|
||||
|
||||
if Self::基础检查(左, 中, 右) {
|
||||
// Python: 序号 = 虚线序列.index(左)
|
||||
let 序号 = 虚线序列.iter().position(|x| Rc::as_ptr(x) == Rc::as_ptr(左)).unwrap_or(i - 1);
|
||||
let 序号 = 虚线序列
|
||||
.iter()
|
||||
.position(|x| Rc::as_ptr(x) == Rc::as_ptr(左))
|
||||
.unwrap_or(i - 1);
|
||||
if 跳过首部 && (左.序号 == 0 || 序号 == 0) {
|
||||
continue;
|
||||
}
|
||||
if 序号 >= 2 {
|
||||
let 前 = &虚线序列[序号 - 2];
|
||||
let 同向相对关系 = crate::types::相对方向::分析(前.高(), 前.低(), 左.高(), 左.低());
|
||||
let 同向相对关系 =
|
||||
crate::types::相对方向::分析(前.高(), 前.低(), 左.高(), 左.低());
|
||||
if 同向相对关系.是否向上() && 左.方向().是否向上() {
|
||||
continue;
|
||||
}
|
||||
@@ -259,46 +419,58 @@ impl 中枢 {
|
||||
}
|
||||
|
||||
// 增量更新
|
||||
let 当前中枢_idx = 中枢序列.len() - 1;
|
||||
let 当前中枢 = Rc::clone(&中枢序列[当前中枢_idx]);
|
||||
let mut 当前中枢_idx = 中枢序列.len() - 1;
|
||||
|
||||
if !当前中枢.校验合法性(虚线序列, 中枢序列) {
|
||||
// Validate in-place via Rc::make_mut — avoids full中枢 struct clone
|
||||
let needs_pop = {
|
||||
let cur = Rc::make_mut(&mut 中枢序列[当前中枢_idx]);
|
||||
!cur.校验合法性(虚线序列)
|
||||
};
|
||||
if needs_pop {
|
||||
let 当前中枢 = Rc::clone(&中枢序列[当前中枢_idx]);
|
||||
Self::从中枢序列尾部弹出(中枢序列, &当前中枢);
|
||||
Self::分析(虚线序列, 中枢序列, 跳过首部, 标识, 层级);
|
||||
return;
|
||||
}
|
||||
|
||||
// 找到当前中枢最后一个元素在虚线序列中的位置
|
||||
let 最后元素 = &当前中枢.基础序列[当前中枢.基础序列.len() - 1];
|
||||
let 起始索引 = match 虚线序列
|
||||
.iter()
|
||||
.position(|x| Rc::as_ptr(x) == Rc::as_ptr(最后元素))
|
||||
{
|
||||
Some(idx) => idx + 1,
|
||||
None => return,
|
||||
let 起始索引 = {
|
||||
let cur = &中枢序列[当前中枢_idx];
|
||||
let 最后元素 = &cur.基础序列[cur.基础序列.len() - 1];
|
||||
match 虚线序列
|
||||
.iter()
|
||||
.position(|x| Rc::as_ptr(x) == Rc::as_ptr(最后元素))
|
||||
{
|
||||
Some(idx) => idx + 1,
|
||||
None => return,
|
||||
}
|
||||
};
|
||||
|
||||
let 中枢高 = 当前中枢.高();
|
||||
let 中枢低 = 当前中枢.低();
|
||||
let mut 中枢高 = 中枢序列[当前中枢_idx].高();
|
||||
let mut 中枢低 = 中枢序列[当前中枢_idx].低();
|
||||
let mut 候选序列: Vec<Rc<虚线>> = Vec::new();
|
||||
|
||||
let mut 新当前中枢 = (*当前中枢).clone();
|
||||
|
||||
for i in 起始索引..虚线序列.len() {
|
||||
let 当前虚线 = Rc::clone(&虚线序列[i]);
|
||||
|
||||
// 检查是否超出中枢范围(缺口)
|
||||
if crate::types::相对方向::分析(中枢高, 中枢低, 当前虚线.高(), 当前虚线.低()).是否缺口() {
|
||||
if crate::types::相对方向::分析(中枢高, 中枢低, 当前虚线.高(), 当前虚线.低()).是否缺口()
|
||||
{
|
||||
候选序列.push(当前虚线.clone());
|
||||
|
||||
// Python: if 当前中枢.基础序列[-1].之后是(当前虚线):
|
||||
if 新当前中枢.基础序列.last().unwrap().之后是(&当前虚线) {
|
||||
新当前中枢.设置第三买卖线(当前虚线.clone());
|
||||
let needs_三买 = {
|
||||
let cur = &中枢序列[当前中枢_idx];
|
||||
cur.基础序列.last().unwrap().之后是(&当前虚线)
|
||||
};
|
||||
if needs_三买 {
|
||||
Rc::make_mut(&mut 中枢序列[当前中枢_idx])
|
||||
.设置第三买卖线(当前虚线.clone());
|
||||
}
|
||||
} else {
|
||||
if 候选序列.is_empty() {
|
||||
// 仍在范围内:延伸中枢
|
||||
新当前中枢.添加虚线(当前虚线);
|
||||
Rc::make_mut(&mut 中枢序列[当前中枢_idx]).添加虚线(当前虚线);
|
||||
} else {
|
||||
候选序列.push(当前虚线);
|
||||
}
|
||||
@@ -306,12 +478,20 @@ impl 中枢 {
|
||||
|
||||
// 候选序列积满3个:尝试创建新中枢
|
||||
while 候选序列.len() >= 3 {
|
||||
let 起始方向 = 新当前中枢.基础序列.last().unwrap().方向().翻转();
|
||||
let 起始方向 = 中枢序列[当前中枢_idx]
|
||||
.基础序列
|
||||
.last()
|
||||
.unwrap()
|
||||
.方向()
|
||||
.翻转();
|
||||
match Self::从序列中获取中枢(&候选序列, 起始方向, 标识) {
|
||||
Some(新中枢) => {
|
||||
中枢序列[当前中枢_idx] = Rc::new(新当前中枢.clone());
|
||||
Self::向中枢序列尾部添加(中枢序列, 新中枢);
|
||||
return;
|
||||
// Python: 当前中枢 = 新中枢
|
||||
当前中枢_idx = 中枢序列.len() - 1;
|
||||
中枢高 = 中枢序列[当前中枢_idx].高();
|
||||
中枢低 = 中枢序列[当前中枢_idx].低();
|
||||
候选序列.clear();
|
||||
}
|
||||
None => {
|
||||
候选序列.remove(0); // 滑动窗口
|
||||
@@ -319,9 +499,6 @@ impl 中枢 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新当前中枢
|
||||
中枢序列[当前中枢_idx] = Rc::new(新当前中枢);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+320
-352
File diff suppressed because it is too large
Load Diff
@@ -11,10 +11,8 @@ use std::collections::HashMap;
|
||||
pub struct 立体分析器 {
|
||||
pub 周期组: Vec<i64>,
|
||||
输入周期: i64,
|
||||
显示周期: i64,
|
||||
K线合成器: K线合成器,
|
||||
单体分析器: HashMap<i64, 观察者>,
|
||||
合成K线计数: HashMap<i64, usize>,
|
||||
}
|
||||
|
||||
impl 立体分析器 {
|
||||
@@ -27,11 +25,6 @@ impl 立体分析器 {
|
||||
let mut 周期组 = 周期组;
|
||||
周期组.sort();
|
||||
let 输入周期 = 周期组[0];
|
||||
let 显示周期 = if 周期组.len() > 1 {
|
||||
周期组[1]
|
||||
} else {
|
||||
周期组[0]
|
||||
};
|
||||
|
||||
let 默认配置 = 配置.unwrap_or_default();
|
||||
let 配置组 = 配置组.unwrap_or_default();
|
||||
@@ -40,50 +33,28 @@ impl 立体分析器 {
|
||||
|
||||
let mut 单体分析器 = HashMap::new();
|
||||
for &周期 in &周期组 {
|
||||
let mut 当前配置 = 配置组.get(&周期).cloned().unwrap_or_else(|| 默认配置.clone());
|
||||
let mut 当前配置 = 配置组
|
||||
.get(&周期)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| 默认配置.clone());
|
||||
当前配置.推送K线 = false;
|
||||
当前配置.推送线段 = false;
|
||||
// Set 标识 to match 符号
|
||||
当前配置.标识 = 符号.clone();
|
||||
|
||||
let 观察员 = 观察者::new(符号.clone(), 周期, 当前配置);
|
||||
单体分析器.insert(周期, 观察员);
|
||||
}
|
||||
|
||||
// Configure display period
|
||||
if let Some(显示观察员) = 单体分析器.get_mut(&显示周期) {
|
||||
显示观察员.配置.推送K线 = true;
|
||||
显示观察员.配置.推送笔 = true;
|
||||
显示观察员.配置.推送线段 = true;
|
||||
显示观察员.配置.图表展示 = true;
|
||||
显示观察员.重置基础序列();
|
||||
}
|
||||
|
||||
// Align other periods to display period's 缠K序列
|
||||
// (in practice, this is done during data loading)
|
||||
for &周期 in &周期组 {
|
||||
if 周期 != 显示周期 {
|
||||
// Other periods will reference display period's 缠论K线序列
|
||||
// This is done during the K-line callback flow
|
||||
}
|
||||
}
|
||||
|
||||
let mut 合成K线计数 = HashMap::new();
|
||||
for &周期 in &周期组 {
|
||||
合成K线计数.insert(周期, 0);
|
||||
}
|
||||
|
||||
Self {
|
||||
周期组,
|
||||
输入周期,
|
||||
显示周期,
|
||||
K线合成器,
|
||||
单体分析器,
|
||||
合成K线计数,
|
||||
}
|
||||
}
|
||||
|
||||
/// 投喂K线 — 统一入口,接收最小周期K线
|
||||
/// 匹配 Python __K线回调:合成器完成K线时喂给观察者
|
||||
pub fn 投喂K线(&mut self, 普K: K线) {
|
||||
if 普K.周期 != self.输入周期 {
|
||||
eprintln!(
|
||||
@@ -93,36 +64,13 @@ impl 立体分析器 {
|
||||
return;
|
||||
}
|
||||
|
||||
// Record current K-line counts before feeding
|
||||
let mut 之前计数 = HashMap::new();
|
||||
for &周期 in &self.周期组 {
|
||||
之前计数.insert(周期, self.K线合成器.合成K线列表.get(&周期).map(|v| v.len()).unwrap_or(0));
|
||||
}
|
||||
// Feed to synthesizer, get completion events
|
||||
let 完成事件 = self.K线合成器.投喂K线(普K);
|
||||
|
||||
// Feed to synthesizer
|
||||
self.K线合成器.投喂K线(普K);
|
||||
|
||||
// Dispatch new K-lines to observers
|
||||
for &周期 in &self.周期组 {
|
||||
let 新K线列表 = self.K线合成器.合成K线列表.get(&周期);
|
||||
let 之前计数 = 之前计数[&周期];
|
||||
if let Some(列表) = 新K线列表 {
|
||||
for k线 in 列表.iter().skip(之前计数) {
|
||||
// Clone the K-line for feeding to observer
|
||||
let 完成K线 = k线.clone();
|
||||
if let Some(观察员) = self.单体分析器.get_mut(&周期) {
|
||||
观察员.增加原始K线(完成K线);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also feed the current in-progress K-line (if any)
|
||||
if let Some(Some(当前K线)) = self.K线合成器.当前K线.get(&周期).cloned() {
|
||||
if let Some(观察员) = self.单体分析器.get_mut(&周期) {
|
||||
// Feed a clone of current in-progress K-line for real-time updates
|
||||
// Note: this may cause duplicate data, matching Python behavior
|
||||
观察员.增加原始K线(当前K线);
|
||||
}
|
||||
// Dispatch on completion events (matching Python's __K线回调)
|
||||
for (周期, 完成K线) in 完成事件 {
|
||||
if let Some(观察员) = self.单体分析器.get_mut(&周期) {
|
||||
观察员.增加原始K线(完成K线);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,12 +85,49 @@ impl 立体分析器 {
|
||||
self.单体分析器.get_mut(&周期)
|
||||
}
|
||||
|
||||
/// 测试_保存数据
|
||||
/// 测试_保存数据 — 多级别数据拆分保存
|
||||
/// 创建父目录 PyM_{标识}_{起始时间}_{结束时间},各周期观察者保存到子目录
|
||||
pub fn 测试_保存数据(&self) {
|
||||
let 根目录 = std::env::current_dir().unwrap_or_default();
|
||||
|
||||
let 起始时间 = self
|
||||
.单体分析器
|
||||
.get(&self.输入周期)
|
||||
.and_then(|o| o.普通K线序列.first())
|
||||
.map(|k| k.时间戳)
|
||||
.unwrap_or(0);
|
||||
let 结束时间 = self
|
||||
.单体分析器
|
||||
.get(&self.输入周期)
|
||||
.and_then(|o| o.普通K线序列.last())
|
||||
.map(|k| k.时间戳)
|
||||
.unwrap_or(0);
|
||||
let 标识 = self
|
||||
.单体分析器
|
||||
.get(&self.输入周期)
|
||||
.map(|o| o.符号.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
let 周期 = self
|
||||
.单体分析器
|
||||
.get(&self.输入周期)
|
||||
.map(|o| o.周期)
|
||||
.unwrap_or_default();
|
||||
|
||||
let 目录标识 = format!("RustM_{}:{}_{}_{}", 标识, 周期, 起始时间, 结束时间);
|
||||
let 保存路径 = 根目录.join(&目录标识);
|
||||
|
||||
if let Err(e) = std::fs::create_dir_all(&保存路径) {
|
||||
eprintln!("创建目录失败: {} -> {}", 保存路径.display(), e);
|
||||
return;
|
||||
}
|
||||
|
||||
for 周期 in &self.周期组 {
|
||||
if let Some(观察员) = self.单体分析器.get(周期) {
|
||||
观察员.测试_保存数据(None);
|
||||
观察员.测试_保存数据(Some(保存路径.to_str().unwrap()));
|
||||
}
|
||||
}
|
||||
|
||||
println!("多级别数据拆分保存完成,目录:{}", 保存路径.display());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,8 +51,7 @@ pub struct 观察者 {
|
||||
|
||||
impl 观察者 {
|
||||
pub fn new(符号: String, 周期: i64, 配置: 缠论配置) -> Self {
|
||||
let 终止时间戳 = if 配置.手动终止 != "1970-01-01 00:00:00"
|
||||
&& !配置.手动终止.is_empty()
|
||||
let 终止时间戳 = if 配置.手动终止 != "1970-01-01 00:00:00" && !配置.手动终止.is_empty()
|
||||
{
|
||||
datetime::转化为时间戳(&配置.手动终止)
|
||||
} else {
|
||||
@@ -129,10 +128,10 @@ impl 观察者 {
|
||||
}
|
||||
|
||||
/// 核心数据处理管道
|
||||
fn __处理数据(&mut self, mut 普K: K线) {
|
||||
fn __处理数据(&mut self, 普K: K线) {
|
||||
// Step 1: 缠论K线分析 (普K is consumed by 分析 as &mut)
|
||||
let (_, 当前分型) = 缠论K线::分析(
|
||||
&mut 普K,
|
||||
普K,
|
||||
&mut self.缠论K线序列,
|
||||
&mut self.普通K线序列,
|
||||
&self.配置,
|
||||
@@ -175,31 +174,25 @@ impl 观察者 {
|
||||
&[相对方向::向上, 相对方向::向下],
|
||||
);
|
||||
}
|
||||
// Clone to avoid borrow issues
|
||||
let 线段序列_克隆 = self.线段序列.clone();
|
||||
if self.配置.分析线段中枢 {
|
||||
中枢::分析(&线段序列_克隆, &mut self.中枢序列, true, "", 0);
|
||||
中枢::分析(&self.线段序列, &mut self.中枢序列, true, "", 0);
|
||||
}
|
||||
|
||||
// Step 5: 扩展线段(笔级)
|
||||
let 笔序列_克隆 = self.笔序列.clone();
|
||||
if self.配置.分析扩展线段 {
|
||||
线段::扩展分析(&笔序列_克隆, &mut self.扩展线段序列, &self.配置);
|
||||
线段::扩展分析(&self.笔序列, &mut self.扩展线段序列, &self.配置);
|
||||
}
|
||||
let 扩展线段序列_克隆 = self.扩展线段序列.clone();
|
||||
if self.配置.分析线段中枢 {
|
||||
中枢::分析(&扩展线段序列_克隆, &mut self.扩展中枢序列, true, "", 0);
|
||||
中枢::分析(&self.扩展线段序列, &mut self.扩展中枢序列, true, "", 0);
|
||||
}
|
||||
|
||||
// Step 6: 扩展线段(线段级)
|
||||
let 线段序列_克隆2 = self.线段序列.clone();
|
||||
if self.配置.分析扩展线段 {
|
||||
线段::扩展分析(&线段序列_克隆2, &mut self.扩展线段序列_线段, &self.配置);
|
||||
线段::扩展分析(&self.线段序列, &mut self.扩展线段序列_线段, &self.配置);
|
||||
}
|
||||
let 扩展线段_线段_克隆 = self.扩展线段序列_线段.clone();
|
||||
if self.配置.分析线段中枢 {
|
||||
中枢::分析(
|
||||
&扩展线段_线段_克隆,
|
||||
&self.扩展线段序列_线段,
|
||||
&mut self.扩展中枢序列_线段,
|
||||
true,
|
||||
"",
|
||||
@@ -208,10 +201,9 @@ impl 观察者 {
|
||||
}
|
||||
|
||||
// Step 7: 线段之线段
|
||||
let 线段序列_克隆3 = self.线段序列.clone();
|
||||
if self.配置.分析线段 {
|
||||
线段::分析(
|
||||
&线段序列_克隆3,
|
||||
&self.线段序列,
|
||||
&mut self.线段_线段序列,
|
||||
&self.配置,
|
||||
0,
|
||||
@@ -224,30 +216,21 @@ impl 观察者 {
|
||||
],
|
||||
);
|
||||
}
|
||||
let 线段_线段_克隆 = self.线段_线段序列.clone();
|
||||
if self.配置.分析线段中枢 {
|
||||
中枢::分析(
|
||||
&线段_线段_克隆,
|
||||
&mut self.线段_中枢序列,
|
||||
true,
|
||||
"",
|
||||
0,
|
||||
);
|
||||
中枢::分析(&self.线段_线段序列, &mut self.线段_中枢序列, true, "", 0);
|
||||
}
|
||||
|
||||
// Step 8: 扩展线段之扩展线段
|
||||
let 扩展线段序列_克隆2 = self.扩展线段序列.clone();
|
||||
if self.配置.分析扩展线段 {
|
||||
线段::扩展分析(
|
||||
&扩展线段序列_克隆2,
|
||||
&self.扩展线段序列,
|
||||
&mut self.扩展线段序列_扩展线段,
|
||||
&self.配置,
|
||||
);
|
||||
}
|
||||
let 扩展线段_扩展线段_克隆 = self.扩展线段序列_扩展线段.clone();
|
||||
if self.配置.分析线段中枢 {
|
||||
中枢::分析(
|
||||
&扩展线段_扩展线段_克隆,
|
||||
&self.扩展线段序列_扩展线段,
|
||||
&mut self.扩展中枢序列_扩展线段,
|
||||
true,
|
||||
"",
|
||||
@@ -272,21 +255,18 @@ impl 观察者 {
|
||||
self.扩展线段序列_扩展线段.clear();
|
||||
self.扩展中枢序列_扩展线段.clear();
|
||||
|
||||
let 缠K克隆 = self.缠论K线序列.clone();
|
||||
let 普K克隆 = self.普通K线序列.clone();
|
||||
|
||||
for i in 1..缠K克隆.len() - 1 {
|
||||
for i in 1..self.缠论K线序列.len() - 1 {
|
||||
let 当前分型 = 分型::new(
|
||||
Some(Rc::clone(&缠K克隆[i - 1])),
|
||||
Rc::clone(&缠K克隆[i]),
|
||||
Some(Rc::clone(&缠K克隆[i + 1])),
|
||||
Some(Rc::clone(&self.缠论K线序列[i - 1])),
|
||||
Rc::clone(&self.缠论K线序列[i]),
|
||||
Some(Rc::clone(&self.缠论K线序列[i + 1])),
|
||||
);
|
||||
笔::分析(
|
||||
Rc::new(当前分型),
|
||||
&mut self.分型序列,
|
||||
&mut self.笔序列,
|
||||
&缠K克隆,
|
||||
&普K克隆,
|
||||
&self.缠论K线序列,
|
||||
&self.普通K线序列,
|
||||
&self.配置,
|
||||
);
|
||||
}
|
||||
@@ -295,38 +275,32 @@ impl 观察者 {
|
||||
中枢::分析(&self.笔序列, &mut self.笔_中枢序列, true, "", 0);
|
||||
}
|
||||
|
||||
let 笔克隆 = self.笔序列.clone();
|
||||
if self.配置.分析线段 {
|
||||
线段::分析(
|
||||
&笔克隆,
|
||||
&self.笔序列,
|
||||
&mut self.线段序列,
|
||||
&self.配置,
|
||||
0,
|
||||
&[相对方向::向上, 相对方向::向下],
|
||||
);
|
||||
}
|
||||
let 线段克隆 = self.线段序列.clone();
|
||||
if self.配置.分析线段中枢 {
|
||||
中枢::分析(&线段克隆, &mut self.中枢序列, true, "", 0);
|
||||
中枢::分析(&self.线段序列, &mut self.中枢序列, true, "", 0);
|
||||
}
|
||||
|
||||
let 笔克隆2 = self.笔序列.clone();
|
||||
if self.配置.分析扩展线段 {
|
||||
线段::扩展分析(&笔克隆2, &mut self.扩展线段序列, &self.配置);
|
||||
线段::扩展分析(&self.笔序列, &mut self.扩展线段序列, &self.配置);
|
||||
}
|
||||
let 扩展线段克隆 = self.扩展线段序列.clone();
|
||||
if self.配置.分析线段中枢 {
|
||||
中枢::分析(&扩展线段克隆, &mut self.扩展中枢序列, true, "", 0);
|
||||
中枢::分析(&self.扩展线段序列, &mut self.扩展中枢序列, true, "", 0);
|
||||
}
|
||||
|
||||
let 线段克隆2 = self.线段序列.clone();
|
||||
if self.配置.分析扩展线段 {
|
||||
线段::扩展分析(&线段克隆2, &mut self.扩展线段序列_线段, &self.配置);
|
||||
线段::扩展分析(&self.线段序列, &mut self.扩展线段序列_线段, &self.配置);
|
||||
}
|
||||
let 扩展线段_线段_克隆 = self.扩展线段序列_线段.clone();
|
||||
if self.配置.分析线段中枢 {
|
||||
中枢::分析(
|
||||
&扩展线段_线段_克隆,
|
||||
&self.扩展线段序列_线段,
|
||||
&mut self.扩展中枢序列_线段,
|
||||
true,
|
||||
"",
|
||||
@@ -334,10 +308,9 @@ impl 观察者 {
|
||||
);
|
||||
}
|
||||
|
||||
let 线段克隆3 = self.线段序列.clone();
|
||||
if self.配置.分析线段 {
|
||||
线段::分析(
|
||||
&线段克隆3,
|
||||
&self.线段序列,
|
||||
&mut self.线段_线段序列,
|
||||
&self.配置,
|
||||
0,
|
||||
@@ -350,36 +323,18 @@ impl 观察者 {
|
||||
],
|
||||
);
|
||||
}
|
||||
let 线段_线段_克隆 = self.线段_线段序列.clone();
|
||||
if self.配置.分析线段中枢 {
|
||||
中枢::分析(
|
||||
&线段_线段_克隆,
|
||||
&mut self.线段_中枢序列,
|
||||
true,
|
||||
"",
|
||||
0,
|
||||
);
|
||||
中枢::分析(&self.线段_线段序列, &mut self.线段_中枢序列, true, "", 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// 测试_保存数据 — 输出各序列数据文本到文件
|
||||
pub fn 测试_保存数据(&self, root: Option<&str>) {
|
||||
|
||||
let 笔序列_文本数据: Vec<String> = self
|
||||
.笔序列
|
||||
.iter()
|
||||
.map(|b| b.获取数据文本())
|
||||
.collect();
|
||||
let 线段序列_文本数据: Vec<String> = self
|
||||
.线段序列
|
||||
.iter()
|
||||
.map(|s| s.获取数据文本())
|
||||
.collect();
|
||||
let 扩展线段序列_数据文本: Vec<String> = self
|
||||
.扩展线段序列
|
||||
.iter()
|
||||
.map(|s| s.获取数据文本())
|
||||
.collect();
|
||||
let 笔序列_文本数据: Vec<String> = self.笔序列.iter().map(|b| b.获取数据文本()).collect();
|
||||
let 线段序列_文本数据: Vec<String> =
|
||||
self.线段序列.iter().map(|s| s.获取数据文本()).collect();
|
||||
let 扩展线段序列_数据文本: Vec<String> =
|
||||
self.扩展线段序列.iter().map(|s| s.获取数据文本()).collect();
|
||||
let 扩展线段序列_线段_数据文本: Vec<String> = self
|
||||
.扩展线段序列_线段
|
||||
.iter()
|
||||
@@ -396,21 +351,12 @@ impl 观察者 {
|
||||
.map(|s| s.获取数据文本())
|
||||
.collect();
|
||||
|
||||
let 笔_中枢序列_数据文本: Vec<String> = self
|
||||
.笔_中枢序列
|
||||
.iter()
|
||||
.map(|h| h.获取数据文本())
|
||||
.collect();
|
||||
let 中枢序列_数据文本: Vec<String> = self
|
||||
.中枢序列
|
||||
.iter()
|
||||
.map(|h| h.获取数据文本())
|
||||
.collect();
|
||||
let 扩展中枢序列_数据文本: Vec<String> = self
|
||||
.扩展中枢序列
|
||||
.iter()
|
||||
.map(|h| h.获取数据文本())
|
||||
.collect();
|
||||
let 笔_中枢序列_数据文本: Vec<String> =
|
||||
self.笔_中枢序列.iter().map(|h| h.获取数据文本()).collect();
|
||||
let 中枢序列_数据文本: Vec<String> =
|
||||
self.中枢序列.iter().map(|h| h.获取数据文本()).collect();
|
||||
let 扩展中枢序列_数据文本: Vec<String> =
|
||||
self.扩展中枢序列.iter().map(|h| h.获取数据文本()).collect();
|
||||
let 扩展中枢序列_线段_数据文本: Vec<String> = self
|
||||
.扩展中枢序列_线段
|
||||
.iter()
|
||||
@@ -436,7 +382,7 @@ impl 观察者 {
|
||||
// 生成子目录名称
|
||||
let 起始时间 = self.普通K线序列.first().map(|k| k.时间戳).unwrap_or(0);
|
||||
let 结束时间 = self.普通K线序列.last().map(|k| k.时间戳).unwrap_or(0);
|
||||
let 目录标识 = format!("Rust_{}_{}_{}", self.符号, 起始时间, 结束时间);
|
||||
let 目录标识 = format!("Rust_{}:{}_{}_{}", self.符号, self.周期, 起始时间, 结束时间);
|
||||
|
||||
let 保存路径 = 根目录.join(&目录标识);
|
||||
if let Err(e) = std::fs::create_dir_all(&保存路径) {
|
||||
@@ -444,19 +390,57 @@ impl 观察者 {
|
||||
return;
|
||||
}
|
||||
|
||||
// 缠K data for debugging
|
||||
let 缠K序列_数据文本: Vec<String> = self
|
||||
.缠论K线序列
|
||||
.iter()
|
||||
.map(|ck| {
|
||||
format!(
|
||||
"缠K, {}, {}, {:?}, {}, {}, {}, {}, {}",
|
||||
ck.序号,
|
||||
ck.时间戳,
|
||||
ck.分型,
|
||||
ck.方向,
|
||||
ck.高,
|
||||
ck.低,
|
||||
ck.原始起始序号,
|
||||
ck.原始结束序号
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let 分型序列_数据文本: Vec<String> = self
|
||||
.分型序列
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, fx)| {
|
||||
format!(
|
||||
"分型, {}, {}, {:?}, {}, {}, {}",
|
||||
i, fx.时间戳, fx.结构, fx.分型特征值, fx.中.时间戳, fx.中.低,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let 数据映射: Vec<(&str, &[String])> = vec![
|
||||
("笔序列_文本数据", &笔序列_文本数据),
|
||||
("线段序列_文本数据", &线段序列_文本数据),
|
||||
("扩展线段序列_数据文本", &扩展线段序列_数据文本),
|
||||
("扩展线段序列_线段_数据文本", &扩展线段序列_线段_数据文本),
|
||||
("线段_线段序列_数据文本", &线段_线段序列_数据文本),
|
||||
("扩展线段序列_扩展线段_数据文本", &扩展线段序列_扩展线段_数据文本),
|
||||
(
|
||||
"扩展线段序列_扩展线段_数据文本",
|
||||
&扩展线段序列_扩展线段_数据文本,
|
||||
),
|
||||
("笔_中枢序列_数据文本", &笔_中枢序列_数据文本),
|
||||
("中枢序列_数据文本", &中枢序列_数据文本),
|
||||
("扩展中枢序列_数据文本", &扩展中枢序列_数据文本),
|
||||
("扩展中枢序列_线段_数据文本", &扩展中枢序列_线段_数据文本),
|
||||
("线段_中枢序列_数据文本", &线段_中枢序列_数据文本),
|
||||
("扩展中枢序列_扩展线段_数据文本", &扩展中枢序列_扩展线段_数据文本),
|
||||
(
|
||||
"扩展中枢序列_扩展线段_数据文本",
|
||||
&扩展中枢序列_扩展线段_数据文本,
|
||||
),
|
||||
("缠K序列_数据文本", &缠K序列_数据文本),
|
||||
("分型序列_数据文本", &分型序列_数据文本),
|
||||
];
|
||||
|
||||
for (文件名, 数据列表) in &数据映射 {
|
||||
@@ -471,7 +455,9 @@ impl 观察者 {
|
||||
}
|
||||
|
||||
/// 读取数据文件 — 从 .nb 文件加载数据
|
||||
pub fn 读取数据文件(文件路径: &str, 配置: Option<缠论配置>) -> Result<Self, String> {
|
||||
pub fn 读取数据文件(
|
||||
文件路径: &str, 配置: Option<缠论配置>
|
||||
) -> Result<Self, String> {
|
||||
let 配置 = 配置.unwrap_or_default();
|
||||
|
||||
// Parse filename: btcusd-300-1631772074-1632222374.nb
|
||||
@@ -485,7 +471,9 @@ impl 观察者 {
|
||||
return Err(format!("invalid filename format: {}", name));
|
||||
}
|
||||
let 符号 = parts[0].to_string();
|
||||
let 周期: i64 = parts[1].parse().map_err(|e| format!("parse period: {}", e))?;
|
||||
let 周期: i64 = parts[1]
|
||||
.parse()
|
||||
.map_err(|e| format!("parse period: {}", e))?;
|
||||
|
||||
let mut 实例 = Self::new(符号, 周期, 配置);
|
||||
|
||||
|
||||
@@ -30,31 +30,38 @@ impl K线合成器 {
|
||||
}
|
||||
|
||||
/// 投喂K线 — 输入最小周期K线,合成为所有目标周期
|
||||
pub fn 投喂K线(&mut self, 普K: K线) {
|
||||
/// 返回本次投喂完成了哪些周期的K线(周期 → 完成K线)
|
||||
pub fn 投喂K线(&mut self, 普K: K线) -> Vec<(i64, K线)> {
|
||||
let mut 完成记录 = Vec::new();
|
||||
let 周期组 = self.周期组.clone();
|
||||
for 周期 in 周期组 {
|
||||
self._处理单个周期(周期, &普K);
|
||||
if let Some(完成K线) = self._处理单个周期(周期, &普K) {
|
||||
完成记录.push((周期, 完成K线));
|
||||
}
|
||||
}
|
||||
完成记录
|
||||
}
|
||||
|
||||
fn _处理单个周期(&mut self, 周期: i64, 普K: &K线) {
|
||||
fn _处理单个周期(&mut self, 周期: i64, 普K: &K线) -> Option<K线> {
|
||||
let 目标时间戳 = self._对齐时间戳(普K.时间戳, 周期);
|
||||
let 当前K线 = self.当前K线.get(&周期).unwrap().clone();
|
||||
let 相同时间 = self.当前K线[&周期]
|
||||
.as_ref()
|
||||
.map(|k| k.时间戳 == 目标时间戳)
|
||||
.unwrap_or(false);
|
||||
|
||||
if 当前K线.is_none() {
|
||||
// 创建新K线
|
||||
if self.当前K线[&周期].is_none() {
|
||||
let 新K线 = self._创建新K线(周期, 目标时间戳, 普K);
|
||||
*self.当前K线.get_mut(&周期).unwrap() = Some(新K线);
|
||||
} else if 当前K线.as_ref().unwrap().时间戳 == 目标时间戳 {
|
||||
// 更新当前K线
|
||||
let mut k线 = 当前K线.unwrap();
|
||||
self._更新K线(&mut k线, 普K);
|
||||
*self.当前K线.get_mut(&周期).unwrap() = Some(k线);
|
||||
self.当前K线.insert(周期, Some(新K线));
|
||||
None
|
||||
} else if 相同时间 {
|
||||
let ent = self.当前K线.get_mut(&周期).unwrap();
|
||||
Self::_更新K线(ent.as_mut().unwrap(), 普K);
|
||||
None
|
||||
} else {
|
||||
// 完成当前K线,创建新K线
|
||||
self._完成K线(周期);
|
||||
let 完成K线 = self._完成K线(周期);
|
||||
let 新K线 = self._创建新K线(周期, 目标时间戳, 普K);
|
||||
*self.当前K线.get_mut(&周期).unwrap() = Some(新K线);
|
||||
self.当前K线.insert(周期, Some(新K线));
|
||||
完成K线
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,26 +93,26 @@ impl K线合成器 {
|
||||
)
|
||||
}
|
||||
|
||||
fn _更新K线(&self, 当前K线: &mut K线, 新数据: &K线) {
|
||||
fn _更新K线(当前K线: &mut K线, 新数据: &K线) {
|
||||
当前K线.高 = 当前K线.高.max(新数据.高);
|
||||
当前K线.低 = 当前K线.低.min(新数据.低);
|
||||
当前K线.收盘价 = 新数据.收盘价;
|
||||
当前K线.成交量 += 新数据.成交量;
|
||||
}
|
||||
|
||||
fn _完成K线(&mut self, 周期: i64) {
|
||||
let 当前K线 = self.当前K线.get(&周期).and_then(|k| k.clone());
|
||||
if let Some(mut k线) = 当前K线 {
|
||||
k线.序号 = self
|
||||
.合成K线列表
|
||||
.get(&周期)
|
||||
.and_then(|list| list.last())
|
||||
.map(|k| k.序号 + 1)
|
||||
.unwrap_or(0);
|
||||
fn _完成K线(&mut self, 周期: i64) -> Option<K线> {
|
||||
let ent = self.当前K线.get_mut(&周期).unwrap();
|
||||
let mut k线 = ent.take()?;
|
||||
k线.序号 = self
|
||||
.合成K线列表
|
||||
.get(&周期)
|
||||
.and_then(|list| list.last())
|
||||
.map(|k| k.序号 + 1)
|
||||
.unwrap_or(0);
|
||||
|
||||
self.合成K线列表.get_mut(&周期).unwrap().push(k线);
|
||||
*self.当前K线.get_mut(&周期).unwrap() = None;
|
||||
}
|
||||
let 完成K线 = k线.clone();
|
||||
self.合成K线列表.get_mut(&周期).unwrap().push(k线);
|
||||
Some(完成K线)
|
||||
}
|
||||
|
||||
/// 获取指定周期当前正在合成的K线
|
||||
|
||||
+12
-8
@@ -244,26 +244,30 @@ impl 缠论配置 {
|
||||
}
|
||||
|
||||
/// 按序号重组字典 — 兼容旧版配置的复合key格式
|
||||
pub fn 按序号重组字典(默认配置: &Self, 原始字典: &serde_json::Value) -> Vec<(i64, Self)> {
|
||||
pub fn 按序号重组字典(
|
||||
默认配置: &Self,
|
||||
原始字典: &serde_json::Value,
|
||||
) -> Vec<(i64, Self)> {
|
||||
let mut result = Vec::new();
|
||||
if let serde_json::Value::Object(map) = 原始字典 {
|
||||
// 按数字前缀分组: "1_open" → group 1 key "open"
|
||||
let mut groups: std::collections::BTreeMap<i64, serde_json::Map<String, serde_json::Value>> =
|
||||
std::collections::BTreeMap::new();
|
||||
let mut groups: std::collections::BTreeMap<
|
||||
i64,
|
||||
serde_json::Map<String, serde_json::Value>,
|
||||
> = std::collections::BTreeMap::new();
|
||||
for (key, value) in map {
|
||||
if let Some(pos) = key.find('_') {
|
||||
if let Ok(num) = key[..pos].parse::<i64>() {
|
||||
let field = key[pos + 1..].to_string();
|
||||
groups
|
||||
.entry(num)
|
||||
.or_default()
|
||||
.insert(field, value.clone());
|
||||
groups.entry(num).or_default().insert(field, value.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (num, fields) in groups {
|
||||
let mut config = 默认配置.clone();
|
||||
if let Ok(partial) = serde_json::from_value::<缠论配置>(serde_json::Value::Object(fields)) {
|
||||
if let Ok(partial) =
|
||||
serde_json::from_value::<缠论配置>(serde_json::Value::Object(fields))
|
||||
{
|
||||
// merge partial into config (override matching fields)
|
||||
config = partial;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,13 @@ impl 随机指标 {
|
||||
}
|
||||
|
||||
/// 基于前一个 KDJ 增量计算当前 KDJ
|
||||
pub fn 增量计算(前一个KDJ: &Self, 当前最高价: f64, 当前最低价: f64, 当前收盘价: f64, 当前时间: i64) -> Self {
|
||||
pub fn 增量计算(
|
||||
前一个KDJ: &Self,
|
||||
当前最高价: f64,
|
||||
当前最低价: f64,
|
||||
当前收盘价: f64,
|
||||
当前时间: i64,
|
||||
) -> Self {
|
||||
let N = 前一个KDJ.N;
|
||||
let M1 = 前一个KDJ.M1;
|
||||
let M2 = 前一个KDJ.M2;
|
||||
|
||||
@@ -45,7 +45,13 @@ fn 平滑系数(周期: i64) -> f64 {
|
||||
|
||||
impl 平滑异同移动平均线 {
|
||||
/// 首次计算 MACD 指标(无历史数据时使用)
|
||||
pub fn 首次计算(初始收盘价: f64, 初始时间: i64, 快线周期: i64, 慢线周期: i64, 信号周期: i64) -> Self {
|
||||
pub fn 首次计算(
|
||||
初始收盘价: f64,
|
||||
初始时间: i64,
|
||||
快线周期: i64,
|
||||
慢线周期: i64,
|
||||
信号周期: i64,
|
||||
) -> Self {
|
||||
let 快线EMA = 初始收盘价;
|
||||
let 慢线EMA = 初始收盘价;
|
||||
let DIF = 快线EMA - 慢线EMA;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
pub mod macd;
|
||||
pub mod kdj;
|
||||
pub mod macd;
|
||||
pub mod rsi;
|
||||
|
||||
pub use macd::平滑异同移动平均线;
|
||||
pub use kdj::随机指标;
|
||||
pub use macd::平滑异同移动平均线;
|
||||
pub use rsi::相对强弱指数;
|
||||
|
||||
/// K线取值 —— 根据计算方式从K线提取对应的价格
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::indicators::{平滑异同移动平均线, 随机指标, 相对强弱指数};
|
||||
use crate::indicators::{平滑异同移动平均线, 相对强弱指数, 随机指标};
|
||||
use crate::types::相对方向;
|
||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -140,8 +140,14 @@ impl K线 {
|
||||
|
||||
/// 获取两K线之间的 MACD 柱面积
|
||||
pub fn 获取MACD(K线序列: &[&Self], 始: &Self, 终: &Self) -> HashMap<String, f64> {
|
||||
let 始_idx = K线序列.iter().position(|k| std::ptr::eq(*k, 始)).unwrap_or(0);
|
||||
let 终_idx = K线序列.iter().position(|k| std::ptr::eq(*k, 终)).unwrap_or(0);
|
||||
let 始_idx = K线序列
|
||||
.iter()
|
||||
.position(|k| std::ptr::eq(*k, 始))
|
||||
.expect("获取MACD: 始K线不在序列中");
|
||||
let 终_idx = K线序列
|
||||
.iter()
|
||||
.position(|k| std::ptr::eq(*k, 终))
|
||||
.expect("获取MACD: 终K线不在序列中");
|
||||
let 基序 = &K线序列[始_idx..=终_idx];
|
||||
|
||||
let mut 阳 = 0.0f64;
|
||||
@@ -219,7 +225,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_serialization_roundtrip() {
|
||||
let k = K线::创建普K("test", 1600000000, 100.5, 110.2, 95.3, 105.7, 5000.0, 42, 60);
|
||||
let k = K线::创建普K(
|
||||
"test", 1600000000, 100.5, 110.2, 95.3, 105.7, 5000.0, 42, 60,
|
||||
);
|
||||
let bytes = k.to_bytes();
|
||||
let restored = K线::from_bytes(&bytes, 60, "test").unwrap();
|
||||
|
||||
|
||||
+121
-84
@@ -1,5 +1,7 @@
|
||||
use crate::config::缠论配置;
|
||||
use crate::indicators::{K线取值, 平滑异同移动平均线, 随机指标, 相对强弱指数};
|
||||
use crate::indicators::{
|
||||
平滑异同移动平均线, 相对强弱指数, 随机指标, K线取值
|
||||
};
|
||||
use crate::kline::bar::K线;
|
||||
use crate::structure::fractal_obj::分型;
|
||||
use crate::types::分型结构;
|
||||
@@ -142,12 +144,14 @@ impl 缠论K线 {
|
||||
if let Some(基) = 基线.first() {
|
||||
for k in 基线.iter().rev() {
|
||||
if 基.周期 < k线.周期 {
|
||||
if k线.时间戳 <= k.时间戳 && k.时间戳 <= k线.时间戳 + k线.周期 {
|
||||
if k线.时间戳 <= k.时间戳 && k.时间戳 <= k线.时间戳 + k线.周期
|
||||
{
|
||||
if (k线.分型特征值 - k.分型特征值).abs() < f64::EPSILON {
|
||||
return k.时间戳;
|
||||
}
|
||||
}
|
||||
} else if k.时间戳 <= k线.时间戳 && k线.时间戳 <= k.时间戳 + k.周期 {
|
||||
} else if k.时间戳 <= k线.时间戳 && k线.时间戳 <= k.时间戳 + k.周期
|
||||
{
|
||||
if (k线.分型特征值 - k.分型特征值).abs() < f64::EPSILON {
|
||||
return k.时间戳;
|
||||
}
|
||||
@@ -207,7 +211,7 @@ impl 缠论K线 {
|
||||
/// 返回 (新缠K, 模式) — 模式: "添加"/"替换"/None
|
||||
pub fn 兼并(
|
||||
之前缠K: Option<&缠论K线>,
|
||||
当前缠K: &缠论K线,
|
||||
当前缠K: &mut 缠论K线,
|
||||
当前普K: &K线,
|
||||
配置: &缠论配置,
|
||||
) -> (Option<Rc<缠论K线>>, Option<String>) {
|
||||
@@ -234,40 +238,50 @@ impl 缠论K线 {
|
||||
return (Some(Rc::new(新缠K)), Some("添加".into()));
|
||||
}
|
||||
|
||||
// 包含关系 — 合并到当前缠K
|
||||
// 取值方向:向下 → 取低低、高高(即全部取最低);向上 → 取高高、低低(即全部取最高)
|
||||
// 方向由之前缠K与当前缠K的关系决定
|
||||
// 重复提交检测 — 当序号相同时认为是重复提交K线
|
||||
if 当前普K.序号 == 当前缠K.原始结束序号 {
|
||||
return (None, None);
|
||||
}
|
||||
|
||||
// 序号连续性检查
|
||||
if 当前普K.序号 - 1 != 当前缠K.原始结束序号 && 当前普K.序号 != 当前缠K.原始结束序号
|
||||
{
|
||||
panic!(
|
||||
"兼并: 不可追加不连续元素 缠K.原始结束序号: {}, 当前普K.序号: {}",
|
||||
当前缠K.原始结束序号, 当前普K.序号
|
||||
);
|
||||
}
|
||||
|
||||
// 包含关系 — 原地合并到当前缠K
|
||||
let 取值函数: fn(f64, f64) -> f64 = if let Some(之前) = 之前缠K {
|
||||
if 相对方向::分析(之前.高, 之前.低, 当前缠K.高, 当前缠K.低).是否向下() {
|
||||
if 相对方向::分析(之前.高, 之前.低, 当前缠K.高, 当前缠K.低).是否向下()
|
||||
{
|
||||
f64::min
|
||||
} else {
|
||||
f64::max
|
||||
}
|
||||
} else {
|
||||
f64::max // 默认向上取max
|
||||
f64::max
|
||||
};
|
||||
|
||||
let mut 合并后 = 当前缠K.镜像();
|
||||
|
||||
// 逆序包含时更新时间和标的K线
|
||||
if 关系 != 相对方向::顺 {
|
||||
合并后.时间戳 = 当前普K.时间戳;
|
||||
合并后.标的K线 = Rc::new(当前普K.clone());
|
||||
当前缠K.时间戳 = 当前普K.时间戳;
|
||||
当前缠K.标的K线 = Rc::new(当前普K.clone());
|
||||
}
|
||||
合并后.高 = 取值函数(合并后.高, 当前普K.高);
|
||||
合并后.低 = 取值函数(合并后.低, 当前普K.低);
|
||||
合并后.原始结束序号 = 当前普K.序号;
|
||||
合并后.方向 = 当前普K.方向();
|
||||
当前缠K.高 = 取值函数(当前缠K.高, 当前普K.高);
|
||||
当前缠K.低 = 取值函数(当前缠K.低, 当前普K.低);
|
||||
当前缠K.原始结束序号 = 当前普K.序号;
|
||||
当前缠K.方向 = 当前普K.方向();
|
||||
|
||||
if let Some(之前) = 之前缠K {
|
||||
合并后.序号 = 之前.序号 + 1;
|
||||
当前缠K.序号 = 之前.序号 + 1;
|
||||
}
|
||||
|
||||
if 配置.缠K合并替换 {
|
||||
(Some(Rc::new(合并后)), Some("替换".into()))
|
||||
(Some(Rc::new(当前缠K.镜像())), Some("替换".into()))
|
||||
} else {
|
||||
// 兼并:返回新值让调用方决定是原地修改还是替换
|
||||
(Some(Rc::new(合并后)), Some("兼并".into()))
|
||||
(None, None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,7 +289,7 @@ impl 缠论K线 {
|
||||
///
|
||||
/// 返回 (状态, 形态)
|
||||
pub fn 分析(
|
||||
当前K线: &mut K线,
|
||||
mut 当前K线: K线,
|
||||
缠K序列: &mut Vec<Rc<缠论K线>>,
|
||||
普K序列: &mut Vec<Rc<K线>>,
|
||||
配置: &缠论配置,
|
||||
@@ -286,14 +300,26 @@ impl 缠论K线 {
|
||||
if 普K序列.is_empty() {
|
||||
if 配置.计算指标 {
|
||||
当前K线.macd = Some(平滑异同移动平均线::首次计算(
|
||||
K线取值(当前K线.开盘价, 当前K线.高, 当前K线.低, 当前K线.收盘价, &配置.指标计算方式),
|
||||
K线取值(
|
||||
当前K线.开盘价,
|
||||
当前K线.高,
|
||||
当前K线.低,
|
||||
当前K线.收盘价,
|
||||
&配置.指标计算方式,
|
||||
),
|
||||
当前K线.时间戳,
|
||||
配置.平滑异同移动平均线_快线周期,
|
||||
配置.平滑异同移动平均线_慢线周期,
|
||||
配置.平滑异同移动平均线_信号周期,
|
||||
));
|
||||
当前K线.rsi = Some(相对强弱指数::首次计算(
|
||||
K线取值(当前K线.开盘价, 当前K线.高, 当前K线.低, 当前K线.收盘价, &配置.指标计算方式),
|
||||
K线取值(
|
||||
当前K线.开盘价,
|
||||
当前K线.高,
|
||||
当前K线.低,
|
||||
当前K线.收盘价,
|
||||
&配置.指标计算方式,
|
||||
),
|
||||
当前K线.时间戳,
|
||||
配置.相对强弱指数_周期,
|
||||
配置.相对强弱指数_超买阈值,
|
||||
@@ -312,7 +338,7 @@ impl 缠论K线 {
|
||||
配置.随机指标_超卖阈值,
|
||||
));
|
||||
}
|
||||
let 当前K线_rc = Rc::new(当前K线.clone());
|
||||
let 当前K线_rc = Rc::new(当前K线);
|
||||
普K序列.push(当前K线_rc);
|
||||
} else {
|
||||
let 之前普K = 普K序列.last().unwrap();
|
||||
@@ -324,14 +350,26 @@ impl 缠论K线 {
|
||||
if let Some(ref prev_macd) = 普K序列[普K序列.len() - 2].macd {
|
||||
当前K线.macd = Some(平滑异同移动平均线::增量计算(
|
||||
prev_macd,
|
||||
K线取值(当前K线.开盘价, 当前K线.高, 当前K线.低, 当前K线.收盘价, &配置.指标计算方式),
|
||||
K线取值(
|
||||
当前K线.开盘价,
|
||||
当前K线.高,
|
||||
当前K线.低,
|
||||
当前K线.收盘价,
|
||||
&配置.指标计算方式,
|
||||
),
|
||||
当前K线.时间戳,
|
||||
));
|
||||
}
|
||||
if let Some(ref prev_rsi) = 普K序列[普K序列.len() - 2].rsi {
|
||||
当前K线.rsi = Some(相对强弱指数::增量计算(
|
||||
prev_rsi,
|
||||
K线取值(当前K线.开盘价, 当前K线.高, 当前K线.低, 当前K线.收盘价, &配置.指标计算方式),
|
||||
K线取值(
|
||||
当前K线.开盘价,
|
||||
当前K线.高,
|
||||
当前K线.低,
|
||||
当前K线.收盘价,
|
||||
&配置.指标计算方式,
|
||||
),
|
||||
当前K线.时间戳,
|
||||
));
|
||||
}
|
||||
@@ -347,7 +385,7 @@ impl 缠论K线 {
|
||||
}
|
||||
}
|
||||
普K序列.pop();
|
||||
普K序列.push(Rc::new(当前K线.clone()));
|
||||
普K序列.push(Rc::new(当前K线));
|
||||
} else {
|
||||
if 之前普K.时间戳 > 当前K线.时间戳 {
|
||||
panic!("时序错误: 之前={}, 当前={}", 之前普K.时间戳, 当前K线.时间戳);
|
||||
@@ -357,14 +395,26 @@ impl 缠论K线 {
|
||||
if let Some(ref prev_macd) = 之前普K.macd {
|
||||
当前K线.macd = Some(平滑异同移动平均线::增量计算(
|
||||
prev_macd,
|
||||
K线取值(当前K线.开盘价, 当前K线.高, 当前K线.低, 当前K线.收盘价, &配置.指标计算方式),
|
||||
K线取值(
|
||||
当前K线.开盘价,
|
||||
当前K线.高,
|
||||
当前K线.低,
|
||||
当前K线.收盘价,
|
||||
&配置.指标计算方式,
|
||||
),
|
||||
当前K线.时间戳,
|
||||
));
|
||||
}
|
||||
if let Some(ref prev_rsi) = 之前普K.rsi {
|
||||
当前K线.rsi = Some(相对强弱指数::增量计算(
|
||||
prev_rsi,
|
||||
K线取值(当前K线.开盘价, 当前K线.高, 当前K线.低, 当前K线.收盘价, &配置.指标计算方式),
|
||||
K线取值(
|
||||
当前K线.开盘价,
|
||||
当前K线.高,
|
||||
当前K线.低,
|
||||
当前K线.收盘价,
|
||||
&配置.指标计算方式,
|
||||
),
|
||||
当前K线.时间戳,
|
||||
));
|
||||
}
|
||||
@@ -378,67 +428,48 @@ impl 缠论K线 {
|
||||
));
|
||||
}
|
||||
}
|
||||
普K序列.push(Rc::new(当前K线.clone()));
|
||||
普K序列.push(Rc::new(当前K线));
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 阶段2: 缠K合并 ----
|
||||
let 之前缠K: Option<&缠论K线> = if 缠K序列.len() >= 2 {
|
||||
Some(&缠K序列[缠K序列.len() - 2])
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let 状态: String;
|
||||
let 当前K线_ref: &K线 = &*普K序列.last().unwrap();
|
||||
|
||||
if !缠K序列.is_empty() {
|
||||
let 最后一个缠K = &缠K序列[缠K序列.len() - 1];
|
||||
// 需要从 Rc 中取出引用
|
||||
let 最后一个缠K_ref = Rc::as_ref(最后一个缠K);
|
||||
let (新缠K, 模式) = Self::兼并(之前缠K, 最后一个缠K_ref, 当前K线, 配置);
|
||||
let len = 缠K序列.len();
|
||||
let (左边, 右边) = 缠K序列.split_at_mut(len - 1);
|
||||
let 之前缠K: Option<&缠论K线> = 左边.last().map(|rc| Rc::as_ref(rc));
|
||||
let 最后一个缠K_mut = Rc::make_mut(&mut 右边[0]);
|
||||
let (新缠K, 模式) = Self::兼并(之前缠K, 最后一个缠K_mut, 当前K线_ref, 配置);
|
||||
|
||||
if let Some(k) = 新缠K {
|
||||
if let Some(ref m) = 模式 {
|
||||
if m == "添加" {
|
||||
match 模式.as_deref() {
|
||||
Some("添加") => {
|
||||
缠K序列.push(k);
|
||||
状态 = "创建".into();
|
||||
} else if m == "替换" {
|
||||
}
|
||||
Some("替换") => {
|
||||
缠K序列.pop();
|
||||
缠K序列.push(k);
|
||||
状态 = "替换".into();
|
||||
} else {
|
||||
// 兼并:尝试原地修改以保持Rc指针不变
|
||||
let 最后_idx = 缠K序列.len() - 1;
|
||||
if let Some(原地) = Rc::get_mut(&mut 缠K序列[最后_idx]) {
|
||||
原地.高 = k.高;
|
||||
原地.低 = k.低;
|
||||
原地.时间戳 = k.时间戳;
|
||||
原地.方向 = k.方向;
|
||||
原地.原始结束序号 = k.原始结束序号;
|
||||
原地.标的K线 = k.标的K线.clone();
|
||||
原地.序号 = k.序号;
|
||||
状态 = "兼并".into();
|
||||
} else {
|
||||
// 有其他引用时回退到替换
|
||||
缠K序列.pop();
|
||||
缠K序列.push(k);
|
||||
状态 = "兼并".into();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
状态 = "兼并".into();
|
||||
_ => {
|
||||
状态 = "兼并".into();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
状态 = "兼并".into();
|
||||
}
|
||||
} else {
|
||||
let 新缠K = Self::创建缠K(
|
||||
当前K线.时间戳,
|
||||
当前K线.高,
|
||||
当前K线.低,
|
||||
当前K线.方向(),
|
||||
当前K线_ref.时间戳,
|
||||
当前K线_ref.高,
|
||||
当前K线_ref.低,
|
||||
当前K线_ref.方向(),
|
||||
None,
|
||||
当前K线.序号,
|
||||
Rc::new(当前K线.clone()),
|
||||
当前K线_ref.序号,
|
||||
Rc::clone(普K序列.last().unwrap()),
|
||||
None,
|
||||
);
|
||||
缠K序列.push(Rc::new(新缠K));
|
||||
@@ -511,10 +542,10 @@ impl 缠论K线 {
|
||||
}
|
||||
|
||||
let 形态 = if matches!(结构, 分型结构::上 | 分型结构::下) {
|
||||
// 三连方向不算完整分型 — 只有中+右
|
||||
// Python: 形态 = 分型(中, 右, None) — 左=中K线, 中=右K线, 右=None
|
||||
Rc::new(分型::new(
|
||||
None,
|
||||
Rc::clone(&缠K序列[idx - 2]),
|
||||
Some(Rc::clone(&缠K序列[idx - 2])),
|
||||
Rc::clone(&缠K序列[idx - 1]),
|
||||
None,
|
||||
))
|
||||
} else {
|
||||
@@ -532,9 +563,15 @@ impl 缠论K线 {
|
||||
}
|
||||
|
||||
/// 截取缠K序列从始到终
|
||||
pub fn 截取(序列: &[Rc<缠论K线>], 始: &缠论K线, 终: &缠论K线) -> Option<Vec<Rc<缠论K线>>> {
|
||||
let 始_idx = 序列.iter().position(|k| Rc::as_ptr(k) == (始 as *const _))?;
|
||||
let 终_idx = 序列.iter().position(|k| Rc::as_ptr(k) == (终 as *const _))?;
|
||||
pub fn 截取(
|
||||
序列: &[Rc<缠论K线>], 始: &缠论K线, 终: &缠论K线
|
||||
) -> Option<Vec<Rc<缠论K线>>> {
|
||||
let 始_idx = 序列
|
||||
.iter()
|
||||
.position(|k| Rc::as_ptr(k) == (始 as *const _))?;
|
||||
let 终_idx = 序列
|
||||
.iter()
|
||||
.position(|k| Rc::as_ptr(k) == (终 as *const _))?;
|
||||
Some(序列[始_idx..=终_idx].to_vec())
|
||||
}
|
||||
}
|
||||
@@ -569,11 +606,11 @@ mod tests {
|
||||
#[test]
|
||||
fn test_分析_empty_sequence() {
|
||||
let config = 缠论配置::default();
|
||||
let mut pk = make_普K(1000, 100.0, 110.0, 95.0, 105.0, 0);
|
||||
let pk = make_普K(1000, 100.0, 110.0, 95.0, 105.0, 0);
|
||||
let mut 缠K序列 = Vec::new();
|
||||
let mut 普K序列 = Vec::new();
|
||||
|
||||
let (状态, 形态) = 缠论K线::分析(&mut pk, &mut 缠K序列, &mut 普K序列, &config);
|
||||
let (状态, 形态) = 缠论K线::分析(pk, &mut 缠K序列, &mut 普K序列, &config);
|
||||
assert_eq!(状态, "新建");
|
||||
assert_eq!(缠K序列.len(), 1);
|
||||
assert!(形态.is_none()); // 不够3根
|
||||
@@ -586,16 +623,16 @@ mod tests {
|
||||
let mut 普K序列 = Vec::new();
|
||||
|
||||
// 三根形成顶分型: 低高 → 更高高 → 低高
|
||||
let mut pk1 = make_普K(1000, 100.0, 110.0, 95.0, 105.0, 0);
|
||||
let 状态1 = 缠论K线::分析(&mut pk1, &mut 缠K序列, &mut 普K序列, &config);
|
||||
let pk1 = make_普K(1000, 100.0, 110.0, 95.0, 105.0, 0);
|
||||
let 状态1 = 缠论K线::分析(pk1, &mut 缠K序列, &mut 普K序列, &config);
|
||||
assert_eq!(状态1.0, "新建");
|
||||
|
||||
let mut pk2 = make_普K(1001, 105.0, 115.0, 102.0, 112.0, 1);
|
||||
let 状态2 = 缠论K线::分析(&mut pk2, &mut 缠K序列, &mut 普K序列, &config);
|
||||
let pk2 = make_普K(1001, 105.0, 115.0, 102.0, 112.0, 1);
|
||||
let 状态2 = 缠论K线::分析(pk2, &mut 缠K序列, &mut 普K序列, &config);
|
||||
assert!(状态2.1.is_none()); // 仍不够
|
||||
|
||||
let mut pk3 = make_普K(1002, 112.0, 113.0, 100.0, 103.0, 2);
|
||||
let (_状态3, 形态) = 缠论K线::分析(&mut pk3, &mut 缠K序列, &mut 普K序列, &config);
|
||||
let pk3 = make_普K(1002, 112.0, 113.0, 100.0, 103.0, 2);
|
||||
let (_状态3, 形态) = 缠论K线::分析(pk3, &mut 缠K序列, &mut 普K序列, &config);
|
||||
assert!(形态.is_some()); // 分型产生了
|
||||
}
|
||||
}
|
||||
|
||||
+126
-6
@@ -1,29 +1,82 @@
|
||||
use chanlun::business::multi_frame::立体分析器;
|
||||
use chanlun::business::observer::观察者;
|
||||
use chanlun::config::缠论配置;
|
||||
use chanlun::kline::bar::K线;
|
||||
use std::env;
|
||||
use std::time::Instant;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() < 2 {
|
||||
eprintln!("用法: {} <.nb文件路径>", args[0]);
|
||||
eprintln!("用法:");
|
||||
eprintln!(
|
||||
" {} read <.nb文件路径> 测试_读取数据(单周期分析)",
|
||||
args[0]
|
||||
);
|
||||
eprintln!(
|
||||
" {} synth <.nb文件路径> 测试_周期合成(多周期合成)",
|
||||
args[0]
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let 文件路径 = &args[1];
|
||||
println!("读取文件: {}", 文件路径);
|
||||
let 命令 = &args[1];
|
||||
|
||||
match 观察者::读取数据文件(文件路径, None) {
|
||||
match 命令.as_str() {
|
||||
"read" | "r" => {
|
||||
if args.len() < 3 {
|
||||
eprintln!("用法: {} read <.nb文件路径>", args[0]);
|
||||
std::process::exit(1);
|
||||
}
|
||||
测试_读取数据(&args[2]);
|
||||
}
|
||||
"synth" | "s" => {
|
||||
if args.len() < 3 {
|
||||
eprintln!("用法: {} synth <.nb文件路径>", args[0]);
|
||||
std::process::exit(1);
|
||||
}
|
||||
测试_周期合成(&args[2]);
|
||||
}
|
||||
// Backward compatibility: if first arg is a file path, treat as "read"
|
||||
other if other.ends_with(".nb") => {
|
||||
测试_读取数据(other);
|
||||
}
|
||||
_ => {
|
||||
eprintln!("未知命令: {},请使用 read 或 synth", 命令);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 测试_读取数据 — 单周期分析
|
||||
fn 测试_读取数据(文件路径: &str) {
|
||||
let 启动时间 = Instant::now();
|
||||
|
||||
let 配置 = 缠论配置::default().不推送();
|
||||
match 观察者::读取数据文件(文件路径, Some(配置)) {
|
||||
Ok(观察员) => {
|
||||
let 消耗用时 = 启动时间.elapsed();
|
||||
println!(
|
||||
"测试_读取数据 耗时 {:.2?} 普K数量 {}",
|
||||
消耗用时,
|
||||
观察员.普通K线序列.len()
|
||||
);
|
||||
println!("符号: {}", 观察员.符号);
|
||||
println!("周期: {}", 观察员.周期);
|
||||
println!("普K数量: {}", 观察员.普通K线序列.len());
|
||||
println!("缠K数量: {}", 观察员.缠论K线序列.len());
|
||||
println!("分型数量: {}", 观察员.分型序列.len());
|
||||
println!("笔数量: {}", 观察员.笔序列.len());
|
||||
println!("笔中枢数量: {}", 观察员.笔_中枢序列.len());
|
||||
println!("线段数量: {}", 观察员.线段序列.len());
|
||||
println!("中枢数量: {}", 观察员.中枢序列.len());
|
||||
println!("扩展线段数量: {}", 观察员.扩展线段序列.len());
|
||||
println!("线段_线段序列数量: {}", 观察员.线段_线段序列.len());
|
||||
println!(
|
||||
"扩展线段_扩展线段数量: {}",
|
||||
观察员.扩展线段序列_扩展线段.len()
|
||||
);
|
||||
|
||||
println!("\n===== 数据分析 =====\n");
|
||||
println!("\n===== 保存分析数据 =====\n");
|
||||
观察员.测试_保存数据(None);
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -32,3 +85,70 @@ fn main() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 测试_周期合成 — 多周期合成分析
|
||||
fn 测试_周期合成(文件路径: &str) {
|
||||
let 启动时间 = Instant::now();
|
||||
|
||||
// Parse filename to extract metadata
|
||||
let path = std::path::Path::new(文件路径);
|
||||
let name = path
|
||||
.file_stem()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or("unknown");
|
||||
let parts: Vec<&str> = name.split('-').collect();
|
||||
if parts.len() < 4 {
|
||||
eprintln!(
|
||||
"invalid filename format: {}, expected 符号-周期-起始-结束",
|
||||
name
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
let 符号 = parts[0].to_string();
|
||||
let 周期: i64 = parts[1].parse().unwrap_or(300);
|
||||
let 周期组 = vec![周期, 周期 * 5, 周期 * 5 * 6];
|
||||
|
||||
let 配置 = 缠论配置::default().不推送();
|
||||
let mut 多级别分析 = 立体分析器::new(符号.clone(), 周期组, Some(配置), None);
|
||||
|
||||
// Read binary file and feed K-lines
|
||||
let data = match std::fs::read(文件路径) {
|
||||
Ok(d) => d,
|
||||
Err(e) => {
|
||||
eprintln!("读取文件失败: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let size: usize = 48; // 6 × f64 big-endian
|
||||
let k线数量 = data.len() / size;
|
||||
for i in 0..k线数量 {
|
||||
let offset = i * size;
|
||||
if let Some(k线) = K线::from_bytes(&data[offset..offset + size], 周期, &符号) {
|
||||
多级别分析.投喂K线(k线);
|
||||
}
|
||||
}
|
||||
|
||||
let 消耗用时 = 启动时间.elapsed();
|
||||
println!("测试_周期合成 耗时 {:.2?} 普K数量 {}", 消耗用时, k线数量);
|
||||
println!("符号: {}", 符号);
|
||||
println!("周期组: {:?}", vec![周期, 周期 * 5, 周期 * 5 * 6]);
|
||||
|
||||
// Display stats per period
|
||||
for &p in &[周期, 周期 * 5, 周期 * 5 * 6] {
|
||||
if let Some(观察员) = 多级别分析.获取观察者(p) {
|
||||
println!(
|
||||
"周期<{}>: 缠K={}, 分型={}, 笔={}, 线段={}, 中枢={}",
|
||||
p,
|
||||
观察员.缠论K线序列.len(),
|
||||
观察员.分型序列.len(),
|
||||
观察员.笔序列.len(),
|
||||
观察员.线段序列.len(),
|
||||
观察员.中枢序列.len(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
println!("\n===== 保存分析数据 =====\n");
|
||||
多级别分析.测试_保存数据();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::algorithm::hub::中枢;
|
||||
use crate::config::缠论配置;
|
||||
use crate::kline::chan_kline::缠论K线;
|
||||
use crate::kline::bar::K线;
|
||||
use crate::kline::chan_kline::缠论K线;
|
||||
use crate::structure::fractal_obj::分型;
|
||||
use crate::structure::segment_feat::线段特征;
|
||||
use crate::types::{分型结构, 相对方向, 缺口};
|
||||
@@ -32,7 +32,14 @@ pub struct 虚线 {
|
||||
}
|
||||
|
||||
impl 虚线 {
|
||||
pub fn new(序号: i64, 标识: String, 文: Rc<分型>, 武: Rc<分型>, 级别: i64, 有效性: bool) -> Self {
|
||||
pub fn new(
|
||||
序号: i64,
|
||||
标识: String,
|
||||
文: Rc<分型>,
|
||||
武: Rc<分型>,
|
||||
级别: i64,
|
||||
有效性: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
序号,
|
||||
标识,
|
||||
@@ -60,7 +67,10 @@ impl 虚线 {
|
||||
}
|
||||
|
||||
pub fn 图表标题(&self) -> String {
|
||||
format!("{}:{}:{}:{}", self.文.中.标识, self.文.中.周期, self.标识, self.序号)
|
||||
format!(
|
||||
"{}:{}:{}:{}",
|
||||
self.文.中.标识, self.文.中.周期, self.标识, self.序号
|
||||
)
|
||||
}
|
||||
|
||||
/// 方向 — 文到武的方向
|
||||
@@ -74,24 +84,38 @@ impl 虚线 {
|
||||
}
|
||||
}
|
||||
|
||||
/// 虚线高 — 文和武中高的那个
|
||||
/// 虚线高
|
||||
pub fn 高(&self) -> f64 {
|
||||
self.武.分型特征值.max(self.文.分型特征值)
|
||||
if self.方向() == 相对方向::向下 {
|
||||
self.文.中.高
|
||||
} else {
|
||||
self.武.中.高
|
||||
}
|
||||
}
|
||||
|
||||
/// 虚线低 — 文和武中低的那个
|
||||
/// 虚线低
|
||||
pub fn 低(&self) -> f64 {
|
||||
self.武.分型特征值.min(self.文.分型特征值)
|
||||
if self.方向() == 相对方向::向下 {
|
||||
self.武.中.低
|
||||
} else {
|
||||
self.文.中.低
|
||||
}
|
||||
}
|
||||
|
||||
/// 判断两个虚线是否首尾相连
|
||||
pub fn 之前是(&self, 之前: &虚线) -> bool {
|
||||
self.文.中.序号 == 之前.武.中.序号
|
||||
if self.标识 != 之前.标识 {
|
||||
return false;
|
||||
}
|
||||
Rc::as_ptr(&之前.武) == Rc::as_ptr(&self.文)
|
||||
}
|
||||
|
||||
/// 判断两个虚线是否首尾相连
|
||||
pub fn 之后是(&self, 之后: &虚线) -> bool {
|
||||
self.武.中.序号 == 之后.文.中.序号
|
||||
if self.标识 != 之后.标识 {
|
||||
return false;
|
||||
}
|
||||
Rc::as_ptr(&self.武) == Rc::as_ptr(&之后.文)
|
||||
}
|
||||
|
||||
/// 获取该虚线范围内的普K序列
|
||||
@@ -128,10 +152,15 @@ impl 虚线 {
|
||||
}
|
||||
|
||||
// 非笔:线段/扩展线段等,完整输出
|
||||
let (前, 后, 三, 贯穿伤) =
|
||||
crate::algorithm::segment::线段::分割序列(self, None);
|
||||
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 特征_bool = |b: bool| -> &str {
|
||||
if b {
|
||||
"True"
|
||||
} else {
|
||||
"False"
|
||||
}
|
||||
};
|
||||
|
||||
let 前一缺口_str = match &self.前一缺口 {
|
||||
Some(g) => format!("{}", g),
|
||||
@@ -143,13 +172,52 @@ impl 虚线 {
|
||||
};
|
||||
|
||||
// Format中枢序列 as Python-style list representations
|
||||
let 实_str = format!("[{}]", self.实_中枢序列.iter().map(|h| format!("{}", h)).collect::<Vec<_>>().join(", "));
|
||||
let 虚_str = format!("[{}]", self.虚_中枢序列.iter().map(|h| format!("{}", h)).collect::<Vec<_>>().join(", "));
|
||||
let 合_str = format!("[{}]", self.合_中枢序列.iter().map(|h| format!("{}", h)).collect::<Vec<_>>().join(", "));
|
||||
let 实_str = format!(
|
||||
"[{}]",
|
||||
self.实_中枢序列
|
||||
.iter()
|
||||
.map(|h| format!("{}", h))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
);
|
||||
let 虚_str = format!(
|
||||
"[{}]",
|
||||
self.虚_中枢序列
|
||||
.iter()
|
||||
.map(|h| format!("{}", h))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
);
|
||||
let 合_str = format!(
|
||||
"[{}]",
|
||||
self.合_中枢序列
|
||||
.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(", "));
|
||||
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!(
|
||||
"{}, {}, {}, 文:({},{}), 武:({},{}), {}, {}, ({}, {}, {}), (前: {}, 后: {}, 三: {}, 伤: {}), 实: {}, 虚: {}, 合: {}, {}, {}, {}, {}",
|
||||
@@ -217,7 +285,11 @@ impl 虚线 {
|
||||
|
||||
/// 买卖点配置匹配 — 根据配置中的指标开关组合判断
|
||||
pub fn 买卖点配置匹配(缠K: &缠论K线, 配置: &缠论配置) -> bool {
|
||||
match (配置.买卖点_指标匹配_MACD, 配置.买卖点_指标匹配_KDJ, 配置.买卖点_指标匹配_RSI) {
|
||||
match (
|
||||
配置.买卖点_指标匹配_MACD,
|
||||
配置.买卖点_指标匹配_KDJ,
|
||||
配置.买卖点_指标匹配_RSI,
|
||||
) {
|
||||
(true, true, true) => 缠K.与MACD柱子匹配() && 缠K.与KDJ匹配() && 缠K.与RSI匹配(),
|
||||
(false, false, false) => false,
|
||||
(true, false, true) => 缠K.与MACD柱子匹配() && 缠K.与RSI匹配(),
|
||||
@@ -241,11 +313,7 @@ impl 虚线 {
|
||||
|
||||
/// 买卖点相对匹配 — 至少两个指标匹配
|
||||
pub fn 买卖点相对匹配(缠K: &缠论K线) -> bool {
|
||||
let 混沌槽 = [
|
||||
缠K.与MACD柱子匹配(),
|
||||
缠K.与KDJ匹配(),
|
||||
缠K.与RSI匹配(),
|
||||
];
|
||||
let 混沌槽 = [缠K.与MACD柱子匹配(), 缠K.与KDJ匹配(), 缠K.与RSI匹配()];
|
||||
混沌槽.iter().filter(|&&x| x).count() >= 2
|
||||
}
|
||||
|
||||
@@ -368,7 +436,9 @@ impl 虚线 {
|
||||
// ---- MACD趋向背驰 ----
|
||||
|
||||
/// 计算K线序列MACD趋向背驰 — 分析 MACD柱/DIF/DEA 三项背驰信号
|
||||
pub fn 计算K线序列MACD趋向背驰(普K序列: &[Rc<K线>], 方向: 相对方向) -> [bool; 3] {
|
||||
pub fn 计算K线序列MACD趋向背驰(
|
||||
普K序列: &[Rc<K线>], 方向: 相对方向
|
||||
) -> [bool; 3] {
|
||||
if 普K序列.is_empty() {
|
||||
return [false, false, false];
|
||||
}
|
||||
@@ -389,7 +459,10 @@ impl 虚线 {
|
||||
let 最高柱子 = 柱子序列
|
||||
.iter()
|
||||
.max_by(|a, b| {
|
||||
a.macd.as_ref().unwrap().MACD柱
|
||||
a.macd
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.MACD柱
|
||||
.partial_cmp(&b.macd.as_ref().unwrap().MACD柱)
|
||||
.unwrap()
|
||||
})
|
||||
@@ -402,7 +475,7 @@ impl 虚线 {
|
||||
}
|
||||
}
|
||||
|
||||
// DIF背驰
|
||||
// DIF背驰 (no sort — Python compares peak vs last directly)
|
||||
let 最高离差值 = 柱子序列
|
||||
.iter()
|
||||
.max_by(|a, b| {
|
||||
@@ -411,17 +484,15 @@ impl 虚线 {
|
||||
da.partial_cmp(&db).unwrap()
|
||||
})
|
||||
.unwrap();
|
||||
let mut 柱对2 = vec![Rc::clone(*最高离差值), Rc::clone(最后)];
|
||||
柱对2.sort_by_key(|k| k.时间戳);
|
||||
if let (Some(m0), Some(m1)) = (柱对2[0].macd.as_ref(), 柱对2[1].macd.as_ref()) {
|
||||
if let (Some(m0), Some(m1)) = (最高离差值.macd.as_ref(), 最后.macd.as_ref()) {
|
||||
let dif0 = m0.DIF.unwrap_or(0.0);
|
||||
let dif1 = m1.DIF.unwrap_or(0.0);
|
||||
if dif0 > dif1 && 柱对2[0].高 < 柱对2[1].高 {
|
||||
if dif0 > dif1 && 最高离差值.高 < 最后.高 {
|
||||
结果[1] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// DEA背驰
|
||||
// DEA背驰 (no sort — Python compares peak vs last directly)
|
||||
let 最高信号线 = 柱子序列
|
||||
.iter()
|
||||
.max_by(|a, b| {
|
||||
@@ -430,12 +501,10 @@ impl 虚线 {
|
||||
da.partial_cmp(&db).unwrap()
|
||||
})
|
||||
.unwrap();
|
||||
let mut 柱对3 = vec![Rc::clone(*最高信号线), Rc::clone(最后)];
|
||||
柱对3.sort_by_key(|k| k.时间戳);
|
||||
if let (Some(m0), Some(m1)) = (柱对3[0].macd.as_ref(), 柱对3[1].macd.as_ref()) {
|
||||
if let (Some(m0), Some(m1)) = (最高信号线.macd.as_ref(), 最后.macd.as_ref()) {
|
||||
let dea0 = m0.DEA.unwrap_or(0.0);
|
||||
let dea1 = m1.DEA.unwrap_or(0.0);
|
||||
if dea0 > dea1 && 柱对3[0].高 < 柱对3[1].高 {
|
||||
if dea0 > dea1 && 最高信号线.高 < 最后.高 {
|
||||
结果[2] = true;
|
||||
}
|
||||
}
|
||||
@@ -456,7 +525,11 @@ impl 虚线 {
|
||||
let 最高柱子 = 柱子序列
|
||||
.iter()
|
||||
.max_by(|a, b| {
|
||||
a.macd.as_ref().unwrap().MACD柱.abs()
|
||||
a.macd
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.MACD柱
|
||||
.abs()
|
||||
.partial_cmp(&b.macd.as_ref().unwrap().MACD柱.abs())
|
||||
.unwrap()
|
||||
})
|
||||
@@ -469,7 +542,7 @@ impl 虚线 {
|
||||
}
|
||||
}
|
||||
|
||||
// DIF背驰
|
||||
// DIF背驰 (no sort — Python compares peak vs last directly)
|
||||
let 最高离差值 = 柱子序列
|
||||
.iter()
|
||||
.max_by(|a, b| {
|
||||
@@ -478,17 +551,15 @@ impl 虚线 {
|
||||
da.partial_cmp(&db).unwrap()
|
||||
})
|
||||
.unwrap();
|
||||
let mut 柱对2 = vec![Rc::clone(*最高离差值), Rc::clone(最后)];
|
||||
柱对2.sort_by_key(|k| k.时间戳);
|
||||
if let (Some(m0), Some(m1)) = (柱对2[0].macd.as_ref(), 柱对2[1].macd.as_ref()) {
|
||||
if let (Some(m0), Some(m1)) = (最高离差值.macd.as_ref(), 最后.macd.as_ref()) {
|
||||
let dif0 = m0.DIF.unwrap_or(0.0);
|
||||
let dif1 = m1.DIF.unwrap_or(0.0);
|
||||
if dif0 < dif1 && 柱对2[0].低 > 柱对2[1].低 {
|
||||
if dif0 < dif1 && 最高离差值.低 > 最后.低 {
|
||||
结果[1] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// DEA背驰
|
||||
// DEA背驰 (no sort — Python compares peak vs last directly)
|
||||
let 最高信号线 = 柱子序列
|
||||
.iter()
|
||||
.max_by(|a, b| {
|
||||
@@ -497,12 +568,10 @@ impl 虚线 {
|
||||
da.partial_cmp(&db).unwrap()
|
||||
})
|
||||
.unwrap();
|
||||
let mut 柱对3 = vec![Rc::clone(*最高信号线), Rc::clone(最后)];
|
||||
柱对3.sort_by_key(|k| k.时间戳);
|
||||
if let (Some(m0), Some(m1)) = (柱对3[0].macd.as_ref(), 柱对3[1].macd.as_ref()) {
|
||||
if let (Some(m0), Some(m1)) = (最高信号线.macd.as_ref(), 最后.macd.as_ref()) {
|
||||
let dea0 = m0.DEA.unwrap_or(0.0);
|
||||
let dea1 = m1.DEA.unwrap_or(0.0);
|
||||
if dea0 < dea1 && 柱对3[0].低 > 柱对3[1].低 {
|
||||
if dea0 < dea1 && 最高信号线.低 > 最后.低 {
|
||||
结果[2] = true;
|
||||
}
|
||||
}
|
||||
@@ -520,7 +589,11 @@ impl 虚线 {
|
||||
}
|
||||
|
||||
let 符号 = |x: f64| -> &str {
|
||||
if x > 0.0 { "正" } else { "负" }
|
||||
if x > 0.0 {
|
||||
"正"
|
||||
} else {
|
||||
"负"
|
||||
}
|
||||
};
|
||||
|
||||
let 首_MACD = match &k线序列[0].macd {
|
||||
@@ -592,7 +665,11 @@ impl 虚线 {
|
||||
// ---- 统计MACD行为 ----
|
||||
|
||||
/// 统计MACD行为 — 分析DIF/DEA穿零轴和金叉死叉
|
||||
pub fn 统计MACD行为(普K序列: &[Rc<K线>], 最大间隔: usize, 最少交叉数: usize) -> std::collections::HashMap<String, String> {
|
||||
pub fn 统计MACD行为(
|
||||
普K序列: &[Rc<K线>],
|
||||
最大间隔: usize,
|
||||
最少交叉数: usize,
|
||||
) -> std::collections::HashMap<String, String> {
|
||||
use std::collections::HashMap;
|
||||
|
||||
let mut dif_up = 0;
|
||||
@@ -610,12 +687,20 @@ impl 虚线 {
|
||||
let (pre_dea, cur_dea) = (pre.as_ref().unwrap().DEA, cur.as_ref().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 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; }
|
||||
if pd < 0.0 && cd >= 0.0 {
|
||||
dea_up += 1;
|
||||
}
|
||||
if pd > 0.0 && cd <= 0.0 {
|
||||
dea_down += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -668,11 +753,14 @@ impl 虚线 {
|
||||
/// 买卖意义 — 核心买卖点判断逻辑
|
||||
///
|
||||
/// 返回 (是否有意义, 原因字符串)
|
||||
pub fn 买卖意义(实线: &虚线, 观察员: &crate::business::observer::观察者) -> (bool, String) {
|
||||
pub fn 买卖意义(
|
||||
实线: &虚线, 观察员: &crate::business::observer::观察者
|
||||
) -> (bool, String) {
|
||||
let 普K序列 = &观察员.普通K线序列;
|
||||
let 配置 = &观察员.配置;
|
||||
|
||||
if 实线.标识 != "笔" && 实线.标识 != "线段" && !实线.标识.starts_with("线段<") {
|
||||
if 实线.标识 != "笔" && 实线.标识 != "线段" && !实线.标识.starts_with("线段<")
|
||||
{
|
||||
return (false, "标识不在范围内".into());
|
||||
}
|
||||
|
||||
@@ -718,10 +806,7 @@ impl 虚线 {
|
||||
|
||||
if !结果 && 意义 && 实线.武.中.与MACD柱子匹配() {
|
||||
if Self::武之MACD极值(普K序列, 实线) && 背驰过.len() > 2 {
|
||||
return (
|
||||
true,
|
||||
"没结果, 极值, 柱子分型匹配, 背驰过大于2次".into(),
|
||||
);
|
||||
return (true, "没结果, 极值, 柱子分型匹配, 背驰过大于2次".into());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,12 @@ pub struct 特征分型 {
|
||||
}
|
||||
|
||||
impl 特征分型 {
|
||||
pub fn new(左: Rc<线段特征>, 中: Rc<线段特征>, 右: Rc<线段特征>, 结构: 分型结构) -> Self {
|
||||
Self { 左, 中, 右, 结构 }
|
||||
pub fn new(
|
||||
左: Rc<线段特征>, 中: Rc<线段特征>, 右: Rc<线段特征>, 结构: 分型结构
|
||||
) -> Self {
|
||||
Self {
|
||||
左, 中, 右, 结构
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,9 @@ pub struct 分型 {
|
||||
}
|
||||
|
||||
impl 分型 {
|
||||
pub fn new(左: Option<Rc<缠论K线>>, 中: Rc<缠论K线>, 右: Option<Rc<缠论K线>>) -> Self {
|
||||
pub fn new(
|
||||
左: Option<Rc<缠论K线>>, 中: Rc<缠论K线>, 右: Option<Rc<缠论K线>>
|
||||
) -> Self {
|
||||
let 结构 = 中.分型.unwrap_or(分型结构::散);
|
||||
let 时间戳 = 中.时间戳;
|
||||
let 分型特征值 = 中.分型特征值;
|
||||
@@ -40,59 +42,93 @@ impl 分型 {
|
||||
))
|
||||
}
|
||||
|
||||
/// 分型强度
|
||||
pub fn 强度(&self) -> f64 {
|
||||
match self.结构 {
|
||||
分型结构::顶 | 分型结构::上 => {
|
||||
if let Some(ref 左) = self.左 {
|
||||
self.中.高 - 左.高
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
分型结构::底 | 分型结构::下 => {
|
||||
if let Some(ref 左) = self.左 {
|
||||
左.低 - self.中.低
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
分型结构::散 => 0.0,
|
||||
/// 分型强度 — 返回 "强"/"中"/"弱"/"未知"
|
||||
pub fn 强度(&self) -> &str {
|
||||
if self.结构 != 分型结构::底 && self.结构 != 分型结构::顶 {
|
||||
return "未知";
|
||||
}
|
||||
if self.右.is_none() || self.左.is_none() {
|
||||
return "未知";
|
||||
}
|
||||
|
||||
if let Some(ref 关系组) = self.关系组() {
|
||||
if self.结构 == 分型结构::底 {
|
||||
if 关系组.2.是否向下() {
|
||||
return "弱";
|
||||
} else if 关系组.2.是否向上() {
|
||||
return "强";
|
||||
} else {
|
||||
return "中";
|
||||
}
|
||||
} else if self.结构 == 分型结构::顶 {
|
||||
if 关系组.2.是否向上() {
|
||||
return "弱";
|
||||
} else if 关系组.2.是否向下() {
|
||||
return "强";
|
||||
} else {
|
||||
return "中";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(ref 左), Some(ref 右)) = (&self.左, &self.右) {
|
||||
if self.结构 == 分型结构::底 {
|
||||
if 右.标的K线.收盘价 > 左.标的K线.高 {
|
||||
return "强";
|
||||
} else if 右.标的K线.收盘价 > self.中.标的K线.高 {
|
||||
return "中";
|
||||
} else {
|
||||
return "弱";
|
||||
}
|
||||
} else if self.结构 == 分型结构::顶 {
|
||||
if 右.标的K线.收盘价 < 左.标的K线.低 {
|
||||
return "强";
|
||||
} else if 右.标的K线.收盘价 < self.中.标的K线.低 {
|
||||
return "中";
|
||||
} else {
|
||||
return "弱";
|
||||
}
|
||||
}
|
||||
}
|
||||
"未知"
|
||||
}
|
||||
|
||||
/// MACD柱子分型匹配
|
||||
/// MACD柱子分型匹配 — 检查左中右MACD柱形成顶/底形态
|
||||
pub fn 与MACD柱子分型匹配(&self) -> bool {
|
||||
match self.结构 {
|
||||
分型结构::顶 | 分型结构::上 => {
|
||||
if let Some(ref 左) = self.左 {
|
||||
左.与MACD柱子匹配()
|
||||
} else {
|
||||
false
|
||||
if let (Some(ref 左), Some(ref 右)) = (&self.左, &self.右) {
|
||||
if self.结构 == 分型结构::底 {
|
||||
if let (Some(ref 左macd), Some(ref 中macd), Some(ref 右macd)) =
|
||||
(&左.标的K线.macd, &self.中.标的K线.macd, &右.标的K线.macd)
|
||||
{
|
||||
return 左macd.MACD柱 > 中macd.MACD柱 && 中macd.MACD柱 < 右macd.MACD柱;
|
||||
}
|
||||
}
|
||||
分型结构::底 | 分型结构::下 => {
|
||||
if let Some(ref 右) = self.右 {
|
||||
右.与MACD柱子匹配()
|
||||
} else {
|
||||
false
|
||||
if self.结构 == 分型结构::顶 {
|
||||
if let (Some(ref 左macd), Some(ref 中macd), Some(ref 右macd)) =
|
||||
(&左.标的K线.macd, &self.中.标的K线.macd, &右.标的K线.macd)
|
||||
{
|
||||
return 左macd.MACD柱 < 中macd.MACD柱 && 中macd.MACD柱 > 右macd.MACD柱;
|
||||
}
|
||||
}
|
||||
分型结构::散 => false,
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// 判断两个分型是否匹配
|
||||
pub fn 判断分型(左: &分型, 右: &分型, 模式: &str) -> bool {
|
||||
pub fn 判断分型(左: &Rc<分型>, 右: &Rc<分型>, 模式: &str) -> bool {
|
||||
match 模式 {
|
||||
"中" => 左.中.序号 == 右.中.序号,
|
||||
"中" => Rc::as_ptr(左) == Rc::as_ptr(右),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// 从缠K序列中获取以指定缠K为中元素的分型
|
||||
pub fn 从缠K序列中获取分型(K线序列: &[Rc<缠论K线>], 中: &Rc<缠论K线>) -> Option<Self> {
|
||||
let idx = K线序列.iter().position(|k| Rc::as_ptr(k) == Rc::as_ptr(中))?;
|
||||
pub fn 从缠K序列中获取分型(
|
||||
K线序列: &[Rc<缠论K线>], 中: &Rc<缠论K线>
|
||||
) -> Option<Self> {
|
||||
let idx = K线序列
|
||||
.iter()
|
||||
.position(|k| Rc::as_ptr(k) == Rc::as_ptr(中))?;
|
||||
let 左 = if idx > 0 {
|
||||
Some(Rc::clone(&K线序列[idx - 1]))
|
||||
} else {
|
||||
@@ -108,41 +144,18 @@ impl 分型 {
|
||||
|
||||
/// 向分型序列中添加新分型
|
||||
pub fn 向序列中添加(分型序列: &mut Vec<Rc<分型>>, 当前分型: Rc<分型>) {
|
||||
if let Some(前一个) = 分型序列.last() {
|
||||
if 前一个.时间戳 == 当前分型.时间戳 {
|
||||
// 同一时间戳: 比较强度,保留更强的
|
||||
match 当前分型.结构 {
|
||||
分型结构::顶 | 分型结构::上 => {
|
||||
if 当前分型.分型特征值 >= 前一个.分型特征值 {
|
||||
分型序列.pop();
|
||||
分型序列.push(当前分型);
|
||||
}
|
||||
return;
|
||||
}
|
||||
分型结构::底 | 分型结构::下 => {
|
||||
if 当前分型.分型特征值 <= 前一个.分型特征值 {
|
||||
分型序列.pop();
|
||||
分型序列.push(当前分型);
|
||||
}
|
||||
return;
|
||||
}
|
||||
分型结构::散 => {}
|
||||
}
|
||||
if 分型序列.is_empty() {
|
||||
if 当前分型.结构 != 分型结构::顶 && 当前分型.结构 != 分型结构::底
|
||||
{
|
||||
panic!("首次添加分型不为 顶底 {:?}", 当前分型);
|
||||
}
|
||||
// 相同结构只保留更强的
|
||||
} else {
|
||||
let 前一个 = &分型序列[分型序列.len() - 1];
|
||||
if 前一个.结构 == 当前分型.结构 {
|
||||
if (当前分型.结构 == 分型结构::顶 || 当前分型.结构 == 分型结构::上)
|
||||
&& 当前分型.分型特征值 >= 前一个.分型特征值
|
||||
{
|
||||
分型序列.pop();
|
||||
分型序列.push(当前分型);
|
||||
} else if (当前分型.结构 == 分型结构::底 || 当前分型.结构 == 分型结构::下)
|
||||
&& 当前分型.分型特征值 <= 前一个.分型特征值
|
||||
{
|
||||
分型序列.pop();
|
||||
分型序列.push(当前分型);
|
||||
}
|
||||
return;
|
||||
panic!("分型相同无法添加 {:?} {:?}", 前一个, 当前分型);
|
||||
}
|
||||
if 前一个.右.is_none() {
|
||||
eprintln!("分型.向序列中添加, 分型异常 {:?}", 前一个);
|
||||
}
|
||||
}
|
||||
分型序列.push(当前分型);
|
||||
|
||||
@@ -28,9 +28,9 @@ impl 线段特征 {
|
||||
}
|
||||
|
||||
/// 文 — 取特征序列元素中分型特征值最大/最小的文分型
|
||||
/// tiebreaker: later时间戳 wins when特征值 equal (matches Python)
|
||||
pub fn 文(&self) -> Rc<分型> {
|
||||
if self.线段方向.是否向上() {
|
||||
// 向上 → 取最大的文
|
||||
self.元素
|
||||
.iter()
|
||||
.max_by(|a, b| {
|
||||
@@ -38,11 +38,11 @@ impl 线段特征 {
|
||||
.分型特征值
|
||||
.partial_cmp(&b.文.分型特征值)
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
.then_with(|| a.文.时间戳.cmp(&b.文.时间戳))
|
||||
})
|
||||
.map(|x| Rc::clone(&x.文))
|
||||
.unwrap_or_else(|| Rc::clone(&self.元素[0].文))
|
||||
} else {
|
||||
// 向下 → 取最小的文
|
||||
self.元素
|
||||
.iter()
|
||||
.min_by(|a, b| {
|
||||
@@ -50,6 +50,7 @@ impl 线段特征 {
|
||||
.分型特征值
|
||||
.partial_cmp(&b.文.分型特征值)
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
.then_with(|| b.文.时间戳.cmp(&a.文.时间戳))
|
||||
})
|
||||
.map(|x| Rc::clone(&x.文))
|
||||
.unwrap_or_else(|| Rc::clone(&self.元素[0].文))
|
||||
@@ -57,6 +58,7 @@ impl 线段特征 {
|
||||
}
|
||||
|
||||
/// 武 — 取特征序列元素中分型特征值最大/最小的武分型
|
||||
/// tiebreaker: later时间戳 wins when特征值 equal (matches Python)
|
||||
pub fn 武(&self) -> Rc<分型> {
|
||||
if self.线段方向.是否向上() {
|
||||
self.元素
|
||||
@@ -66,6 +68,7 @@ impl 线段特征 {
|
||||
.分型特征值
|
||||
.partial_cmp(&b.武.分型特征值)
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
.then_with(|| a.武.时间戳.cmp(&b.武.时间戳))
|
||||
})
|
||||
.map(|x| Rc::clone(&x.武))
|
||||
.unwrap_or_else(|| Rc::clone(&self.元素[0].武))
|
||||
@@ -77,6 +80,7 @@ impl 线段特征 {
|
||||
.分型特征值
|
||||
.partial_cmp(&b.武.分型特征值)
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
.then_with(|| b.武.时间戳.cmp(&a.武.时间戳))
|
||||
})
|
||||
.map(|x| Rc::clone(&x.武))
|
||||
.unwrap_or_else(|| Rc::clone(&self.元素[0].武))
|
||||
@@ -109,6 +113,23 @@ impl 线段特征 {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 从特征序列元素中删除虚线
|
||||
pub fn 删除(&mut self, 待删除虚线: &Rc<虚线>) -> Result<(), String> {
|
||||
if 待删除虚线.方向() == self.方向() {
|
||||
return Err("删除方向与特征序列方向相同".into());
|
||||
}
|
||||
if let Some(pos) = self
|
||||
.元素
|
||||
.iter()
|
||||
.position(|x| Rc::as_ptr(x) == Rc::as_ptr(待删除虚线))
|
||||
{
|
||||
self.元素.remove(pos);
|
||||
Ok(())
|
||||
} else {
|
||||
Err("待删除虚线不在特征序列中".into())
|
||||
}
|
||||
}
|
||||
|
||||
/// 新建特征序列元素
|
||||
pub fn 新建(虚线序列: Vec<Rc<虚线>>, 线段方向: 相对方向) -> Self {
|
||||
let 标识 = format!("特征<虚线>");
|
||||
@@ -147,18 +168,8 @@ impl 线段特征 {
|
||||
&& 虚线.低() < 中.低());
|
||||
|
||||
if 应替换 {
|
||||
let 小号虚线 = if 中.元素[0].序号 < 右.元素[0].序号 {
|
||||
&中.元素[0]
|
||||
} else {
|
||||
&右.元素[0]
|
||||
};
|
||||
let 大号虚线 = if 中.元素.last().unwrap().序号
|
||||
> 右.元素.last().unwrap().序号
|
||||
{
|
||||
&中.元素[中.元素.len() - 1]
|
||||
} else {
|
||||
&右.元素[右.元素.len() - 1]
|
||||
};
|
||||
let 小号虚线 = 中.元素.iter().min_by_key(|o| o.序号).unwrap();
|
||||
let 大号虚线 = 右.元素.iter().max_by_key(|o| o.序号).unwrap();
|
||||
let fake = 虚线::创建笔(
|
||||
Rc::clone(&小号虚线.文),
|
||||
Rc::clone(&大号虚线.武),
|
||||
@@ -166,8 +177,7 @@ impl 线段特征 {
|
||||
);
|
||||
结果.pop();
|
||||
let idx = 结果.len() - 1;
|
||||
结果[idx] =
|
||||
Rc::new(Self::新建(vec![Rc::new(fake)], 线段方向));
|
||||
结果[idx] = Rc::new(Self::新建(vec![Rc::new(fake)], 线段方向));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,15 +222,14 @@ impl 线段特征 {
|
||||
let 中 = Rc::clone(&特征序列[i - 1]);
|
||||
let 右 = Rc::clone(&特征序列[i]);
|
||||
|
||||
if let Some(结构) = 分型结构::分析_对象(
|
||||
let 结构 = 分型结构::分析_对象(
|
||||
&*左 as &dyn crate::types::fractal::有高低,
|
||||
&*中 as &dyn crate::types::fractal::有高低,
|
||||
&*右 as &dyn crate::types::fractal::有高低,
|
||||
true,
|
||||
true,
|
||||
) {
|
||||
结果.push(特征分型::new(左, 中, 右, 结构));
|
||||
}
|
||||
);
|
||||
结果.push(特征分型::new(左, 中, 右, 结构.unwrap_or(分型结构::散)));
|
||||
}
|
||||
结果
|
||||
}
|
||||
|
||||
@@ -60,11 +60,11 @@ impl 相对方向 {
|
||||
|
||||
/// 分析两个K线之间的相对方向
|
||||
pub fn 分析(前高: f64, 前低: f64, 后高: f64, 后低: f64) -> Self {
|
||||
if (前高 - 后高).abs() < f64::EPSILON && (前低 - 后低).abs() < f64::EPSILON {
|
||||
if 前高 == 后高 && 前低 == 后低 {
|
||||
return Self::同;
|
||||
}
|
||||
if 前高 > 后高 && 前低 > 后低 {
|
||||
if (前低 - 后高).abs() < f64::EPSILON {
|
||||
if 前低 == 后高 {
|
||||
return Self::衔接向下;
|
||||
}
|
||||
if 前低 > 后高 {
|
||||
@@ -73,7 +73,7 @@ impl 相对方向 {
|
||||
return Self::向下;
|
||||
}
|
||||
if 前高 < 后高 && 前低 < 后低 {
|
||||
if (前高 - 后低).abs() < f64::EPSILON {
|
||||
if 前高 == 后低 {
|
||||
return Self::衔接向上;
|
||||
}
|
||||
if 前高 < 后低 {
|
||||
@@ -87,7 +87,10 @@ impl 相对方向 {
|
||||
if 前高 <= 后高 && 前低 >= 后低 {
|
||||
return Self::逆;
|
||||
}
|
||||
panic!("无法识别的方向: 前({},{}), 后({},{})", 前高, 前低, 后高, 后低);
|
||||
panic!(
|
||||
"无法识别的方向: 前({},{}), 后({},{})",
|
||||
前高, 前低, 后高, 后低
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,16 @@ impl 分型结构 {
|
||||
可以逆序包含: bool,
|
||||
忽视顺序包含: bool,
|
||||
) -> Option<Self> {
|
||||
Self::分析_内部(左.高(), 左.低(), 中.高(), 中.低(), 右.高(), 右.低(), 可以逆序包含, 忽视顺序包含)
|
||||
Self::分析_内部(
|
||||
左.高(),
|
||||
左.低(),
|
||||
中.高(),
|
||||
中.低(),
|
||||
右.高(),
|
||||
右.低(),
|
||||
可以逆序包含,
|
||||
忽视顺序包含,
|
||||
)
|
||||
}
|
||||
|
||||
/// 分析三元素构成的分型结构(trait object 版本)
|
||||
@@ -47,13 +56,25 @@ impl 分型结构 {
|
||||
可以逆序包含: bool,
|
||||
忽视顺序包含: bool,
|
||||
) -> Option<Self> {
|
||||
Self::分析_内部(左.高(), 左.低(), 中.高(), 中.低(), 右.高(), 右.低(), 可以逆序包含, 忽视顺序包含)
|
||||
Self::分析_内部(
|
||||
左.高(),
|
||||
左.低(),
|
||||
中.高(),
|
||||
中.低(),
|
||||
右.高(),
|
||||
右.低(),
|
||||
可以逆序包含,
|
||||
忽视顺序包含,
|
||||
)
|
||||
}
|
||||
|
||||
fn 分析_内部(
|
||||
左高: f64, 左低: f64,
|
||||
中高: f64, 中低: f64,
|
||||
右高: f64, 右低: f64,
|
||||
左高: f64,
|
||||
左低: f64,
|
||||
中高: f64,
|
||||
中低: f64,
|
||||
右高: f64,
|
||||
右低: f64,
|
||||
可以逆序包含: bool,
|
||||
忽视顺序包含: bool,
|
||||
) -> Option<Self> {
|
||||
|
||||
@@ -4,7 +4,11 @@ pub fn format_f64_g(value: f64) -> String {
|
||||
return "nan".to_string();
|
||||
}
|
||||
if value.is_infinite() {
|
||||
return if value > 0.0 { "inf".to_string() } else { "-inf".to_string() };
|
||||
return if value > 0.0 {
|
||||
"inf".to_string()
|
||||
} else {
|
||||
"-inf".to_string()
|
||||
};
|
||||
}
|
||||
|
||||
// Use high precision then trim trailing zeros
|
||||
|
||||
Reference in New Issue
Block a user