第一版

This commit is contained in:
YuWuKunCheng
2026-05-25 02:16:35 +08:00
parent ff4e6ae570
commit f92eef11fb
37 changed files with 11615 additions and 0 deletions
Binary file not shown.
Binary file not shown.
+4184
View File
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "chanlun"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
byteorder = "1"
chrono = { version = "0.4", features = ["serde"] }
+821
View File
@@ -0,0 +1,821 @@
use crate::business::observer::;
use crate::config::;
use crate::kline::bar::K线;
use crate::kline::chan_kline::K线;
use crate::structure::dash_line::线;
use crate::structure::fractal_obj::;
use crate::types::{, };
use std::rc::Rc;
/// 笔 — 从分型生成笔的算法集合(静态方法命名空间)
pub struct ;
impl {
/// 获取可成笔的缠K数量(考虑弱化模式)
pub fn K数量(K序列: &[Rc<K线>], : &[Rc<线>], : &) -> usize {
let = K序列.len();
if >= . as usize {
return ;
}
if . && >= 3 {
let = Self::(K序列, .);
let = Self::(K序列, .);
if let (Some(ref ), Some(ref )) = (&, &) {
let = 1 + (.K线. - .K线.).unsigned_abs() as usize;
if >= . as usize {
return . as usize;
}
}
if !.is_empty() {
// Try both high and low points (Python: 根据缠K找笔(笔序列, 实际高点) or 根据缠K找笔(笔序列, 实际低点))
let = .as_ref()
.and_then(|h| Self::K找笔(, h, 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;
// 向上笔
if .().() && _k. < .() {
if >= ._原始数量 as usize {
return . as usize;
}
}
// 向下笔
if .().() && _k. > .() {
if >= ._原始数量 as usize {
return . as usize;
}
}
}
}
}
}
}
/// 次高 — 排除最高值后的次高点
pub fn (K序列: &[Rc<K线>], : bool) -> Option<Rc<K线>> {
if K序列.len() < 2 {
return K序列.first().cloned();
}
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();
// 按时间戳排序
candidates.sort_by(|a, b| a..cmp(&b.));
if {
Some(Rc::clone(candidates[candidates.len() - 1]))
} else {
Some(Rc::clone(candidates[0]))
}
}
/// 次低 — 排除最低值后的次低点
pub fn (K序列: &[Rc<K线>], : bool) -> Option<Rc<K线>> {
if K序列.len() < 2 {
return K序列.first().cloned();
}
let min_低 = K序列.iter().map(|k| k.).fold(f64::INFINITY, f64::min);
// 排除最低值
let filtered: Vec<&Rc<K线>> = K序列.iter().filter(|k| k. != min_低).collect();
if filtered.is_empty() {
return K序列.first().cloned();
}
// 筛选次低值
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();
// 按时间戳排序
candidates.sort_by(|a, b| a..cmp(&b.));
if {
Some(Rc::clone(candidates[candidates.len() - 1]))
} else {
Some(Rc::clone(candidates[0]))
}
}
/// 实际高点
pub fn (K序列: &[Rc<K线>], : bool) -> Option<Rc<K线>> {
if K序列.is_empty() {
return None;
}
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]));
}
// 按时间戳排序
candidates.sort_by(|a, b| a..cmp(&b.));
if {
Some(Rc::clone(candidates[candidates.len() - 1]))
} else {
Some(Rc::clone(candidates[0]))
}
}
/// 实际低点
pub fn (K序列: &[Rc<K线>], : bool) -> Option<Rc<K线>> {
if K序列.is_empty() {
return None;
}
let min_低 = K序列.iter().map(|k| k.).fold(f64::INFINITY, f64::min);
let mut candidates: Vec<&Rc<K线>> = K序列.iter().filter(|k| k. == min_低).collect();
if candidates.is_empty() {
return Some(Rc::clone(&K序列[0]));
}
// 按时间戳排序
candidates.sort_by(|a, b| a..cmp(&b.));
if {
Some(Rc::clone(candidates[candidates.len() - 1]))
} else {
Some(Rc::clone(candidates[0]))
}
}
/// 判断笔的相对关系是否合理
pub fn (: &线, _配置: &) -> bool {
let = &.;
let = &.;
// 向上笔:文(底)低 → 武(顶)高
if .().() {
return . > .;
}
// 向下笔:文(顶)高 → 武(底)低
. < .
}
/// 以文会友 — 根据起点分型找笔
pub fn (: &[Rc<线>], : &Rc<>) -> Option<Rc<线>> {
.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()
}
/// 根据缠K找对应的笔
pub fn K找笔(: &[Rc<线>], K: &Rc<K线>, : i64) -> Option<Rc<线>> {
// Python iterates in reverse: for 筆 in 笔序列[::-1]
for b in .iter().rev() {
// Python: 筆.文.中.序号 - 偏移 <= 缠K.序号 <= 筆.武.中.序号
if b... - <= K. && K. <= b... {
return Some(Rc::clone(b));
}
}
None
}
/// 从分型序列中弹出最后一个分型和对应的笔
fn (: &mut Vec<Rc<>>, : &mut Vec<Rc<线>>) {
.pop();
if !.is_empty() {
// Python sets旧笔.有效性 = False; with Rc we just drop the笔
.pop();
}
}
/// 核心笔分析 — 使用显式栈模拟递归
///
/// 返回: 递归层次数
pub fn (
: Rc<>,
: &mut Vec<Rc<>>,
: &mut Vec<Rc<线>>,
K序列: &[Rc<K线>],
_普K序列: &[Rc<K线>],
: &,
) -> i64 {
enum {
(Rc<>, i64),
/// 修复错过笔哨兵: 若临时分型被接受为最后一个元素,则扫描武将之后的所有分型
{
: Rc<>,
K: Rc<K线>,
: i64,
},
}
let mut : Vec<> = Vec::new();
.push(::(, 0));
while let Some() = .pop() {
let (, ) = match {
::(fx, lvl) => (fx, lvl),
:: {
,
K,
,
} => {
// Python line 2406: only scan if临时分型 was accepted as last element
if !.is_empty() {
if let Some(last_fx) = .last() {
if Rc::as_ptr(last_fx) == Rc::as_ptr(&) {
if let Some(_idx) = K序列
.iter()
.position(|k| Rc::as_ptr(k) == Rc::as_ptr(&K))
{
let mut : Vec<Rc<>> = Vec::new();
for ck in &K序列[_idx..] {
if ck. == Some(::)
|| ck. == Some(::)
{
if let Some(fx) =
::K序列中获取分型(K序列, ck)
{
.push(Rc::new(fx));
}
}
}
// Push in reverse so first in slice is processed first
for m in .into_iter().rev() {
.push(::(m, + 1));
}
}
}
}
}
// 当前分型 is already next on the stack (pushed before this哨兵)
continue;
}
};
if > 256 {
continue;
}
if !matches!(., :: | ::) {
continue;
}
// Python line 2322-2325: 第一个分型直接追加
if .is_empty() {
.push();
continue;
}
let = Rc::clone(.last().unwrap());
// Python line 2330-2335: 清理无效数据
if . == .
|| matches!(., :: | ::)
{
Self::(, );
if .is_empty() {
if ..is_some() {
.push();
}
continue;
}
}
let = Rc::clone(.last().unwrap());
// Python line 2338: 时序检查 — skip out-of-order fractals
if . > . && .. - .. > 1 {
continue;
}
// Python line 2343-2348: 笔弱化模式
if . && !.is_empty() {
let = .last().unwrap();
let K数 = ... - ... + 1;
if K数 == 3 {
let = (.().()
&& .() > .
&& . == ::)
|| (.().()
&& .() < .
&& . == ::);
if {
Self::(, );
.push(::(, + 1));
continue;
}
}
}
// Re-read之前分型 again after笔弱化 pop
let = Rc::clone(.last().unwrap());
// 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(&.));
if let (Some(_idx), Some(_idx)) = (_idx, _idx) {
let = &K序列[_idx..=_idx];
let K数量 = Self::K数量(, , );
if K数量 >= . as usize {
// Python line 2354-2357: 文官 always uses false (not 笔内相同终点取舍)
let = match . {
:: => Self::(, false),
_ => Self::(, false),
};
// Python line 2359-2367: 文官 != 之前分型.中 → adjust
if let Some(ref _k) = {
if Rc::as_ptr(_k) != Rc::as_ptr(&.) {
if let Some() =
::K序列中获取分型(K序列, _k)
{
.push(::(, ));
.push(::(Rc::new(), + 1));
continue;
}
}
}
// Python line 2369-2372: 武将
let = match . {
:: => Self::(, .),
_ => Self::(, .),
};
let = Rc::new(线::(
Rc::clone(&),
Rc::clone(&),
true,
));
// Python line 2374-2376: 相对关系 and武将 matches
if Self::(&, ) {
if let Some(ref _k) = {
if Rc::as_ptr(_k) == Rc::as_ptr(&.) {
Self::_添加新笔递归(, , , );
continue;
}
}
}
// Python line 2378-2385: 笔次级成笔
if . {
let = match . {
:: => Self::(, .),
_ => Self::(, .),
};
if let Some(ref _k) = {
if Rc::as_ptr(_k) == Rc::as_ptr(&.)
&& Self::(&, )
{
Self::_添加新笔递归(, , , );
continue;
}
}
}
} else {
// Python line 2388-2390: 元素不足 → 右元素扩展
if let Some(ref ) = . {
if let Some() = ::K序列中获取分型(K序列, ) {
.push(::(Rc::new(), + 1));
continue;
}
}
}
}
} else {
// Python line 2392-2419: 分型结构相同 → 更强则替换 + 修复错过笔
let = .;
let = match . {
:: => . < ,
:: => . > ,
_ => false,
};
if {
let = Rc::clone(&);
Self::(, );
if let Some(k线序列) =
K线::(K序列, &., &.)
{
let = match . {
:: => {
Self::(&k线序列, .)
}
_ => Self::(&k线序列, .),
};
if let Some(ref _k) = {
if let Some() =
::K序列中获取分型(K序列, _k)
{
let _rc = Rc::new();
if !.is_empty() {
// Push in reverse processing order (LIFO):
.push(::(Rc::clone(&), + 2));
.push(:: {
: Rc::clone(&_rc),
K: Rc::clone(_k),
: + 1,
});
.push(::(_rc, + 1));
continue;
} else {
.push();
continue;
}
}
}
}
if .is_empty() {
.push();
} else {
.push(::(, + 1));
}
}
}
}
.len() as i64
}
/// 核心笔分析 — 递归实现,逐句对照 chan.py 笔.分析 / 笔递归分析
///
/// 返回: 递归层次数
pub fn (
: Rc<>,
: &mut Vec<Rc<>>,
: &mut Vec<Rc<线>>,
K序列: &[Rc<K线>],
_普K序列: &[Rc<K线>],
: i64,
: &,
) -> i64 {
// Python line 2315-2317: 递归深度限制
if > 64 {
println!("笔.分析 递归深度超出 64 < {}", );
}
// Python line 2319-2320: 非顶底分型跳过
if !matches!(., :: | ::) {
return ;
}
// Python line 2322-2325: 第一个分型直接追加
if .is_empty() {
if matches!(., :: | ::) {
.push();
}
return ;
}
// Python line 2329-2335: 清理无效数据
let = Rc::clone(.last().unwrap());
if . == .
|| matches!(., :: | ::)
{
Self::(, );
if .is_empty() {
if ..is_some() {
::(, );
}
return ;
}
}
// Python line 2337-2341: 时序检查
let = Rc::clone(.last().unwrap());
if . > . && .. - .. > 1 {
println!("时序错误-{}, {}, {}", , , );
return ;
}
// Python line 2343-2348: 笔弱化模式
if . && !.is_empty() {
let = .last().unwrap();
let K数 = ... - ... + 1;
if K数 == 3 {
let = (.().()
&& .() > .
&& . == ::)
|| (.().()
&& .() < .
&& . == ::);
if {
Self::(, );
return Self::(
, , , K序列, _普K序列, + 1, ,
);
}
}
}
// Python line 2350: 分型结构相反 → 可能成笔
let = Rc::clone(.last().unwrap());
if . != . {
if let Some() = K线::(K序列, &., &.) {
let = Rc::new(线::(
Rc::clone(&),
Rc::clone(&),
true,
));
if Self::K数量(&, , ) >= . as usize
{
// Python line 2354-2357: 文官
let = match . {
:: => Self::(&, false),
_ => Self::(&, false),
};
// Python line 2359-2367: 文官调整
if let Some(ref _k) = {
if Rc::as_ptr(_k) != Rc::as_ptr(&.) {
if let Some() =
::K序列中获取分型(K序列, _k)
{
let = Self::(
Rc::new(),
,
,
K序列,
_普K序列,
+ 1,
,
);
return Self::(
, , , K序列, _普K序列,
+ 1,
,
);
}
}
}
// Python line 2369-2375: 武将 and笔形成
let = match . {
:: => {
Self::(&, .)
}
_ => Self::(&, .),
};
if Self::(&, ) {
if let Some(ref _k) = {
if Rc::as_ptr(_k) == Rc::as_ptr(&.) {
// 直接添加(对照 Python _添加新笔:直接 append
Self::_添加新笔递归(, , , );
return ;
}
}
}
// Python line 2378-2385: 笔次级成笔
if . {
let = match . {
:: => {
Self::(&, .)
}
_ => Self::(&, .),
};
if let Some(ref _k) = {
if Rc::as_ptr(_k) == Rc::as_ptr(&.)
&& Self::(&, )
{
Self::_添加新笔递归(, , , );
return ;
}
}
}
} else {
// Python line 2388-2390: 元素不足 → 右元素扩展
if let Some(ref ) = . {
if let Some() =
::K序列中获取分型(K序列, )
{
return Self::(
Rc::new(),
,
,
K序列,
_普K序列,
+ 1,
,
);
}
}
}
}
} else {
// Python line 2392-2419: 分型结构相同 → 更强则替换 + 修复错过笔
let = .;
let = match . {
:: => . < ,
:: => . > ,
_ => false,
};
if {
// 保存被弹出的之前分型(用于修复错过笔的范围计算)
let = Rc::clone(&);
Self::(, );
if let Some(k线序列) =
K线::(K序列, &., &.)
{
let = match . {
:: => {
Self::(&k线序列, .)
}
_ => Self::(&k线序列, .),
};
if let Some(ref _k) = {
if let Some() =
::K序列中获取分型(K序列, _k)
{
let _rc = Rc::new();
if !.is_empty() {
let mut = Self::(
Rc::clone(&_rc),
,
,
K序列,
_普K序列,
+ 1,
,
);
// 修复错过的笔: 扫描武将之后的所有分型
if !.is_empty()
&& Rc::as_ptr(.last().unwrap())
== Rc::as_ptr(&_rc)
{
if let Some(_idx) = K序列
.iter()
.position(|k| Rc::as_ptr(k) == Rc::as_ptr(_k))
{
for ck in &K序列[_idx..] {
if ck. == Some(::)
|| ck. == Some(::)
{
if let Some() =
::K序列中获取分型(K序列, ck)
{
let _rc =
Rc::new();
= Self::(
Rc::clone(&_rc),
,
,
K序列,
_普K序列,
+ 1,
,
);
}
}
}
}
}
return Self::(
, , , K序列, _普K序列,
+ 1,
,
);
} else {
::(, );
}
}
}
} else if .is_empty() {
::(, );
} else {
return Self::(
, , , K序列, _普K序列, + 1, ,
);
}
}
}
}
/// 添加新笔到序列(递归版本 — 直接追加,对应 Python _添加新笔)
fn _添加新笔递归(
: &mut Vec<Rc<>>,
: &mut Vec<Rc<线>>,
: Rc<>,
: Rc<线>,
) {
.push();
let = if .is_empty() {
0
} else {
.last().unwrap(). + 1
};
let mut 线 = (*).clone();
线. = ;
if 线...is_none() && 线...is_none() {
线. = false;
}
.push(Rc::new(线));
}
/// 自检 — 验证笔的有效性(文为实际高/低点,武为实际低/高点)
pub fn (: &线, : &) -> bool {
let = &.;
let = &.;
let = .K序列(&.K线序列);
if Self::K数量(&, , ) >= . as usize {
if .() == :: {
if let (Some(), Some()) = (
Self::(&, false),
Self::(&, .),
) {
if Rc::ptr_eq(&.., &) && Rc::ptr_eq(&.., &) {
return true;
}
}
}
if .() == :: {
if let (Some(), Some()) = (
Self::(&, false),
Self::(&, .),
) {
if Rc::ptr_eq(&.., &) && Rc::ptr_eq(&.., &) {
return true;
}
}
}
}
false
}
/// 获取所有停顿位置 — 在笔范围内找出所有能成笔的分型组合
pub fn (: &线, : &) -> Vec<线> {
let mut = Vec::new();
let = Rc::clone(&.);
let = .K序列(&.K线序列);
if .len() < 5 {
return ;
}
for i in 3...len() - 1 {
let k = &[i];
if k. == Some(::) && .() == :: {
let = Rc::clone(&[i - 1]);
let = Rc::clone(k);
let = Rc::clone(&[i + 1]);
let = ::new(Some(), , Some());
let mut = 线::(Rc::clone(&), Rc::new(), true);
. = .;
if Self::(&, ) {
.push();
}
} else if k. == Some(::) && .() == :: {
let = Rc::clone(&[i - 1]);
let = Rc::clone(k);
let = Rc::clone(&[i + 1]);
let = ::new(Some(), , Some());
let mut = 线::(Rc::clone(&), Rc::new(), true);
. = .;
if Self::(&, ) {
.push();
}
}
}
}
/// 是否背驰过 — 判断笔是否在停顿位置出现过MACD趋向背驰
pub fn (: &线, : &) -> Vec<Rc<>> {
let = Self::(, );
let mut = Vec::new();
for in & {
let k线范围 = K线::rc(
&.K线序列,
&...K线,
&...K线,
);
let = 线::K线序列MACD趋向背驰(&k线范围, .());
if .iter().all(|&x| x) {
.push(Rc::clone(&.));
}
}
}
}
+139
View File
@@ -0,0 +1,139 @@
use crate::config::;
use crate::kline::bar::K线;
use crate::structure::dash_line::线;
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;
if _终 >= K线序列.len() || _终 >= K线序列.len() {
return false;
}
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;
<
}
/// 测度背驰 — 价格测度的背驰判断
pub fn (: &线, : &线) -> bool {
let = (.. - ..).abs();
let = (.. - ..).abs();
<
}
/// 全量背驰 — MACD + 斜率 + 测度 三者全满足
pub fn (: &线, : &线, K序列: &[Rc<K线>]) -> bool {
Self::MACD背驰(, , K序列, "")
&& Self::(, )
&& Self::(, )
}
/// 任意背驰 — 任一条件满足即可
pub fn (: &线, : &线, K序列: &[Rc<K线>]) -> bool {
Self::MACD背驰(, , K序列, "")
|| Self::(, )
|| Self::(, )
}
/// 配置背驰 — 根据配置选择判断方式
pub fn (: &线, : &线, K序列: &[Rc<K线>], : &) -> bool {
let mut result = true;
if .线_MACD {
result = result && Self::MACD背驰(, , K序列, "");
}
if .线_斜率 {
result = result && Self::(, );
}
if .线_测度 {
result = result && Self::(, );
}
result
}
/// 任选背驰
pub fn (: &线, : &线, K序列: &[Rc<K线>]) -> bool {
Self::(, , K序列)
}
/// 背驰模式 — 根据模式字符串选择判断方式
pub fn (
: &线,
: &线,
K序列: &[Rc<K线>],
: &,
: &str,
) -> bool {
match {
"全量" => Self::(, , K序列),
"任意" => Self::(, , K序列),
"配置" => Self::(, , K序列, ),
"相对" => Self::(, ),
_ => Self::(, , K序列, ),
}
}
// ---- 内部辅助 ----
fn (K线序列: &[Rc<K线>], : &str) -> MACD面积 {
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
}
}
}
}
}
MACD面积 { , }
}
}
struct MACD面积 {
: f64,
: f64,
}
+348
View File
@@ -0,0 +1,348 @@
use crate::structure::dash_line::线;
use crate::structure::fractal_obj::;
use crate::types::;
use std::rc::Rc;
/// 中枢 — 三段虚线重叠区间构成的价格中枢
#[derive(Debug, Clone)]
pub struct {
pub : i64,
pub : String,
pub : i64,
pub : Vec<Rc<线>>,
pub 线: Option<Rc<线>>,
pub _第三买卖线: Option<Rc<线>>,
}
impl {
pub fn new(: i64, : String, : i64, : Vec<Rc<线>>) -> Self {
Self {
,
,
,
: .into_iter().take(3).collect(),
线: None,
_第三买卖线: None,
}
}
pub fn 线(&mut self, 线: Rc<线>) {
self..push(线);
self._第三买卖线 = None;
self.线 = None;
}
pub fn (&self) -> String {
format!(
"{}:{}:{}:{}",
self.().., self.().., self., self.
)
}
pub fn (&self) -> Rc<线> {
Rc::clone(&self.[self..len() - 1])
}
pub fn (&self) -> {
self.[0].().()
}
pub fn (&self) -> f64 {
self.[..3]
.iter()
.map(|x| x.())
.min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
.unwrap_or(0.0)
}
pub fn (&self) -> f64 {
self.[..3]
.iter()
.map(|x| x.())
.max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
.unwrap_or(0.0)
}
pub fn (&self) -> f64 {
self.
.iter()
.map(|x| x.())
.max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
.unwrap_or(0.0)
}
pub fn (&self) -> f64 {
self.
.iter()
.map(|x| x.())
.min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
.unwrap_or(0.0)
}
pub fn (&self) -> Rc<> {
Rc::clone(&self.[0].)
}
pub fn (&self) -> Rc<> {
Rc::clone(&self.[self..len() - 1].)
}
pub fn 线(&mut self, 线: Rc<线>) {
self.线 = Some(线);
}
pub fn (&self) -> String {
let 线_str = match &self.线 {
Some(x) => format!("{}", x),
None => "None".to_string(),
};
let _第三买卖线_str = match &self._第三买卖线 {
Some(x) => format!("{}", x),
None => "None".to_string(),
};
format!(
"{}, {}, {}, 文:({},{}), 武:({},{}), {}, {}",
self.,
self.,
self.,
self.().,
crate::utils::format_f64_g(self.().),
self.().,
crate::utils::format_f64_g(self.().),
线_str,
_第三买卖线_str,
)
}
/// 校验中枢合法性
pub fn (&self, 线: &[Rc<线>], _中枢序列: &[Rc<>]) -> bool {
// 1. 检查基础序列中的元素是否仍在虚线序列中
for in &self. {
if !线.iter().any(|x| Rc::as_ptr(x) == Rc::as_ptr()) {
return false;
}
}
// 2. 检查前三根重叠是否有效
if self..len() >= 3 {
let = self.();
let = self.();
if <= {
return false;
}
}
true
}
// ---- 关联函数 ----
/// 基础检查 — 三根虚线是否能形成中枢
pub fn (: &线, : &线, : &线) -> bool {
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);
>
}
/// 创建中枢
pub fn (: Rc<线>, : Rc<线>, : Rc<线>, : i64, : &str) -> Self {
Self::new(0, format!("{}中枢<{}>", , .), , vec![, , ])
}
/// 从序列中获取中枢
pub fn (
线: &[Rc<线>],
: ,
: &str,
) -> Option<Rc<>> {
for i in 2..线.len() {
let = &线[i - 2];
let = &线[i - 1];
let = &线[i];
if Self::(, , ) && .() == {
let = Self::(Rc::clone(), Rc::clone(), Rc::clone(), 0, );
return Some(Rc::new());
}
}
None
}
/// 向中枢序列尾部添加
pub fn (: &mut Vec<Rc<>>, : Rc<>) {
// Dedup
if let Some() = .last() {
if .() == .() && .() == .() {
return;
}
}
// 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();
}
}
/// 从中枢序列尾部弹出
pub fn (: &mut Vec<Rc<>>, : &Rc<>) -> Option<Rc<>> {
if .last().map(|x| Rc::as_ptr(x)) == Some(Rc::as_ptr()) {
.pop()
} else {
None
}
}
/// 中枢分析 — 从虚线序列生成中枢序列(增量算法)
///
/// 每收到新的虚线序列数据后调用,更新中枢序列
pub fn (
线: &[Rc<线>],
: &mut Vec<Rc<>>,
: bool,
: &str,
: i64,
) {
if 线.len() < 3 {
return;
}
// 初始化第一个中枢
if .is_empty() {
for i in 1..线.len() - 1 {
let = &线[i - 1];
let = &线[i];
let = &线[i + 1];
if Self::(, , ) {
// Python: 序号 = 虚线序列.index(左)
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::::(.(), .(), .(), .());
if .() && .().() {
continue;
}
if .() && .().() {
continue;
}
}
let = Rc::new(Self::(
Rc::clone(),
Rc::clone(),
Rc::clone(),
.,
,
));
Self::(, );
// Python: return 中枢递归分析(虚线序列, 中枢序列, ...)
Self::(线, , , , );
return;
}
}
return;
}
// 增量更新
let _idx = .len() - 1;
let = Rc::clone(&[_idx]);
if !.(线, ) {
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 = .();
let mut : Vec<Rc<线>> = Vec::new();
let mut = (*).clone();
for i in ..线.len() {
let 线 = Rc::clone(&线[i]);
// 检查是否超出中枢范围(缺口)
if crate::types::::(, , 线.(), 线.()).() {
.push(线.clone());
// Python: if 当前中枢.基础序列[-1].之后是(当前虚线):
if ..last().unwrap().(&线) {
.线(线.clone());
}
} else {
if .is_empty() {
// 仍在范围内:延伸中枢
.线(线);
} else {
.push(线);
}
}
// 候选序列积满3个:尝试创建新中枢
while .len() >= 3 {
let = ..last().unwrap().().();
match Self::(&, , ) {
Some() => {
[_idx] = Rc::new(.clone());
Self::(, );
return;
}
None => {
.remove(0); // 滑动窗口
}
}
}
}
// 更新当前中枢
[_idx] = Rc::new();
}
}
impl std::fmt::Display for {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let _str = self
.
.iter()
.map(|d| format!("{}", d))
.collect::<Vec<_>>()
.join(", ");
write!(
f,
"{}({}, {}, 元素数量: {}, [{}], {} ===>>> {})",
self.,
crate::utils::format_f64_g(self.()),
crate::utils::format_f64_g(self.()),
self..len(),
_str,
self.(),
self.(),
)
}
}
+4
View File
@@ -0,0 +1,4 @@
pub mod bi;
pub mod divergence;
pub mod hub;
pub mod segment;
File diff suppressed because it is too large Load Diff
+181
View File
@@ -0,0 +1,181 @@
use crate::kline::bar::K线;
use crate::kline::chan_kline::K线;
use crate::structure::fractal_obj::;
use crate::types::bsp_type::;
use crate::types::;
use std::rc::Rc;
/// 基础买卖点 — 买卖点的基础数据结构
#[derive(Debug, Clone)]
pub struct {
pub : String,
pub : ,
pub : Rc<>,
pub K线: Rc<K线>,
pub K线: Rc<K线>,
pub K线: Option<Rc<K线>>,
pub K线: Option<Rc<K线>>,
pub : f64,
pub : Option<>,
}
impl {
pub fn new(
: ,
K线: Rc<K线>,
: Rc<>,
: String,
: f64,
) -> Self {
let K线 = Rc::clone(&.);
Self {
,
,
,
K线,
K线,
K线: None,
K线: None,
: ,
: None,
}
}
/// 偏移 — 当前K线与买卖点K线的序号差
pub fn (&self) -> i64 {
self.K线. - self.K线.
}
/// 失效偏移
pub fn (&self) -> i64 {
match &self.K线 {
Some(k) => k. - self.K线.,
None => -1,
}
}
/// 有效性 — 失效K线是否存在
pub fn (&self) -> bool {
self.K线.is_some()
}
/// 与MACD柱子匹配
pub fn MACD柱子匹配(&self) -> bool {
self.K线.MACD柱子匹配()
}
/// 与MACD柱子分型匹配
pub fn MACD柱子分型匹配(&self) -> bool {
self..MACD柱子分型匹配()
}
}
impl std::fmt::Display for {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}<{}, {}, {}>",
self.,
self.K线.as_ref(),
self.(),
self.(),
)
}
}
/// 买卖点 — 包含一二三类买卖点的工厂方法
pub struct ;
impl {
pub fn (
: Rc<>,
K线: Rc<K线>,
_标识: &str,
: String,
: f64,
) -> {
::new(::, K线, , , )
}
pub fn (
: Rc<>,
K线: Rc<K线>,
_标识: &str,
: String,
: f64,
) -> {
::new(::, K线, , , )
}
pub fn (
: Rc<>,
K线: Rc<K线>,
_标识: &str,
: String,
: f64,
) -> {
::new(::, K线, , , )
}
pub fn (
: Rc<>,
K线: Rc<K线>,
_标识: &str,
: String,
: f64,
) -> {
::new(::, K线, , , )
}
pub fn (
: Rc<>,
K线: Rc<K线>,
_标识: &str,
: String,
: f64,
) -> {
::new(::, K线, , , )
}
pub fn (
: Rc<>,
K线: Rc<K线>,
_标识: &str,
: String,
: f64,
) -> {
::new(::, K线, , , )
}
/// 生成买卖点 — 根据参数自动选择类型
pub fn (
: &str,
: &str,
: &str,
: Rc<>,
K: Rc<K线>,
) -> {
let = if matches!(., :: | ::) {
""
} else {
""
};
let = format!("{}_{}{}{}", , , , );
let = .;
// 当前K线 — 从缠K获取其标的K线
let K线 = Rc::clone(&K.K线);
let = match (, ) {
("", "") => ::,
("", "") => ::,
("", "") => ::,
("", "") => ::,
("", "") => ::,
("", "") => ::,
_ => ::, // fallback
};
::new(, K线, , , )
}
}
+4
View File
@@ -0,0 +1,4 @@
pub mod bsp;
pub mod multi_frame;
pub mod observer;
pub mod synthesizer;
+148
View File
@@ -0,0 +1,148 @@
use crate::business::observer::;
use crate::business::synthesizer::K线合成器;
use crate::config::;
use crate::kline::bar::K线;
use std::collections::HashMap;
/// 立体分析器 — 多周期协调器
///
/// 包含一个K线合成器和每周期一个观察者。
/// 输入最小周期K线,合成大周期后分发到对应观察者。
pub struct {
pub : Vec<i64>,
: i64,
: i64,
K线合成器: K线合成器,
: HashMap<i64, >,
K线计数: HashMap<i64, usize>,
}
impl {
pub fn new(
: String,
: Vec<i64>,
: Option<>,
: Option<HashMap<i64, >>,
) -> Self {
let mut = ;
.sort();
let = [0];
let = if .len() > 1 {
[1]
} else {
[0]
};
let = .unwrap_or_default();
let = .unwrap_or_default();
let K线合成器 = K线合成器::new(.clone(), .clone());
let mut = HashMap::new();
for & in & {
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线
pub fn K线(&mut self, K: K线) {
if K. != self. {
eprintln!(
"立体分析器.投喂K线 周期不匹配 {} != {}",
K., self.
);
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
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线);
}
}
}
}
/// 获取指定周期的观察者
pub fn (&self, : i64) -> Option<&> {
self..get(&)
}
/// 获取指定周期的观察者(可变)
pub fn _mut(&mut self, : i64) -> Option<&mut > {
self..get_mut(&)
}
/// 测试_保存数据
pub fn _保存数据(&self) {
for in &self. {
if let Some() = self..get() {
._保存数据(None);
}
}
}
}
+503
View File
@@ -0,0 +1,503 @@
use crate::algorithm::bi::;
use crate::algorithm::hub::;
use crate::algorithm::segment::线;
use crate::config::;
use crate::kline::bar::K线;
use crate::kline::chan_kline::K线;
use crate::structure::dash_line::线;
use crate::structure::fractal_obj::;
use crate::types::;
use crate::utils::datetime;
use std::rc::Rc;
/// 观察者 — 单周期分析器,持有所有层级序列,接收K线流式输入后逐层计算
pub struct {
pub : String,
pub : i64,
pub : ,
// K线序列
pub K线序列: Vec<Rc<K线>>,
pub K线序列: Vec<Rc<K线>>,
// 分型与笔
pub : Vec<Rc<>>,
pub : Vec<Rc<线>>,
pub _中枢序列: Vec<Rc<>>,
// 线段
pub 线: Vec<Rc<线>>,
pub : Vec<Rc<>>,
// 扩展线段(笔级)
pub 线: Vec<Rc<线>>,
pub : Vec<Rc<>>,
// 扩展线段(线段级)
pub 线_线段: Vec<Rc<线>>,
pub _线段: Vec<Rc<>>,
// 线段之线段
pub 线_线段序列: Vec<Rc<线>>,
pub 线_中枢序列: Vec<Rc<>>,
// 扩展线段之扩展线段
pub 线_扩展线段: Vec<Rc<线>>,
pub _扩展线段: Vec<Rc<>>,
// 终止时间戳
: Option<i64>,
}
impl {
pub fn new(: String, : i64, : ) -> Self {
let = if . != "1970-01-01 00:00:00"
&& !..is_empty()
{
datetime::(&.)
} else {
None
};
let mut instance = Self {
: .clone(),
,
,
K线序列: Vec::new(),
K线序列: Vec::new(),
: Vec::new(),
: Vec::new(),
_中枢序列: Vec::new(),
线: Vec::new(),
: Vec::new(),
线: Vec::new(),
: Vec::new(),
线_线段: Vec::new(),
_线段: Vec::new(),
线_线段序列: Vec::new(),
线_中枢序列: Vec::new(),
线_扩展线段: Vec::new(),
_扩展线段: Vec::new(),
,
};
instance.. = ;
instance
}
/// 标识
pub fn (&self) -> String {
format!("{}:{}", self., self.)
}
/// 当前K线
pub fn K线(&self) -> Option<&Rc<K线>> {
self.K线序列.last()
}
/// 当前缠K
pub fn K(&self) -> Option<&Rc<K线>> {
self.K线序列.last()
}
/// 重置基础序列
pub fn (&mut self) {
self.K线序列.clear();
self.K线序列.clear();
self..clear();
self..clear();
self._中枢序列.clear();
self.线.clear();
self..clear();
self.线.clear();
self..clear();
self.线_线段.clear();
self._线段.clear();
self.线_线段序列.clear();
self.线_中枢序列.clear();
self.线_扩展线段.clear();
self._扩展线段.clear();
}
/// 增加原始K线 — 单根K线投喂入口
pub fn K线(&mut self, K: K线) {
if let Some() = self. {
if K. > {
return;
}
}
self.__处理数据(K);
}
/// 核心数据处理管道
fn __处理数据(&mut self, mut K: K线) {
// Step 1: 缠论K线分析 (普K is consumed by 分析 as &mut)
let (_, ) = K线::(
&mut K,
&mut self.K线序列,
&mut self.K线序列,
&self.,
);
let = match {
Some(fx) => fx,
None => return,
};
// Step 2: 笔分析
if self.. {
::(
,
&mut self.,
&mut self.,
&self.K线序列,
&self.K线序列,
&self.,
);
}
if self..is_empty() {
return;
}
// Step 3: 笔中枢分析
if self.. {
::(&self., &mut self._中枢序列, true, "", 0);
}
if self..is_empty() {
return;
}
// Step 4: 线段分析
if self..线 {
线::(
&self.,
&mut self.线,
&self.,
0,
&[::, ::],
);
}
// Clone to avoid borrow issues
let 线_克隆 = self.线.clone();
if self..线 {
::(&线_克隆, &mut self., true, "", 0);
}
// Step 5: 扩展线段(笔级)
let _克隆 = self..clone();
if self..线 {
线::(&_克隆, &mut self.线, &self.);
}
let 线_克隆 = self.线.clone();
if self..线 {
::(&线_克隆, &mut self., true, "", 0);
}
// Step 6: 扩展线段(线段级)
let 线_克隆2 = self.线.clone();
if self..线 {
线::(&线_克隆2, &mut self.线_线段, &self.);
}
let 线_线段_克隆 = self.线_线段.clone();
if self..线 {
::(
&线_线段_克隆,
&mut self._线段,
true,
"",
0,
);
}
// Step 7: 线段之线段
let 线_克隆3 = self.线.clone();
if self..线 {
线::(
&线_克隆3,
&mut self.线_线段序列,
&self.,
0,
&[
::,
::,
::,
::,
::,
],
);
}
let 线_线段_克隆 = self.线_线段序列.clone();
if self..线 {
::(
&线_线段_克隆,
&mut self.线_中枢序列,
true,
"",
0,
);
}
// Step 8: 扩展线段之扩展线段
let 线_克隆2 = self.线.clone();
if self..线 {
线::(
&线_克隆2,
&mut self.线_扩展线段,
&self.,
);
}
let 线_扩展线段_克隆 = self.线_扩展线段.clone();
if self..线 {
::(
&线_扩展线段_克隆,
&mut self._扩展线段,
true,
"",
0,
);
}
}
/// 静态重新分析 — 遍历所有缠K重新生成分型/笔/线段
pub fn (&mut self) {
self..clear();
self..clear();
self._中枢序列.clear();
self.线.clear();
self..clear();
self.线.clear();
self..clear();
self.线_线段.clear();
self._线段.clear();
self.线_线段序列.clear();
self.线_中枢序列.clear();
self.线_扩展线段.clear();
self._扩展线段.clear();
let K克隆 = self.K线序列.clone();
let K克隆 = self.K线序列.clone();
for i in 1..K克隆.len() - 1 {
let = ::new(
Some(Rc::clone(&K克隆[i - 1])),
Rc::clone(&K克隆[i]),
Some(Rc::clone(&K克隆[i + 1])),
);
::(
Rc::new(),
&mut self.,
&mut self.,
&K克隆,
&K克隆,
&self.,
);
}
if self.. {
::(&self., &mut self._中枢序列, true, "", 0);
}
let = self..clone();
if self..线 {
线::(
&,
&mut self.线,
&self.,
0,
&[::, ::],
);
}
let 线 = self.线.clone();
if self..线 {
::(&线, &mut self., true, "", 0);
}
let 2 = self..clone();
if self..线 {
线::(&2, &mut self.线, &self.);
}
let 线 = self.线.clone();
if self..线 {
::(&线, &mut self., true, "", 0);
}
let 线2 = self.线.clone();
if self..线 {
线::(&线2, &mut self.线_线段, &self.);
}
let 线_线段_克隆 = self.线_线段.clone();
if self..线 {
::(
&线_线段_克隆,
&mut self._线段,
true,
"",
0,
);
}
let 线3 = self.线.clone();
if self..线 {
线::(
&线3,
&mut self.线_线段序列,
&self.,
0,
&[
::,
::,
::,
::,
::,
],
);
}
let 线_线段_克隆 = self.线_线段序列.clone();
if 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(|s| s.())
.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(|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 = match root {
Some(r) => std::path::PathBuf::from(r),
None => std::env::current_dir().unwrap_or_default(),
};
// 生成子目录名称
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 = .join(&);
if let Err(e) = std::fs::create_dir_all(&) {
eprintln!("创建目录失败: {} -> {}", .display(), e);
return;
}
let : Vec<(&str, &[String])> = vec![
("笔序列_文本数据", &_文本数据),
("线段序列_文本数据", &线_文本数据),
("扩展线段序列_数据文本", &线_数据文本),
("扩展线段序列_线段_数据文本", &线_线段_数据文本),
("线段_线段序列_数据文本", &线_线段序列_数据文本),
("扩展线段序列_扩展线段_数据文本", &线_扩展线段_数据文本),
("笔_中枢序列_数据文本", &_中枢序列_数据文本),
("中枢序列_数据文本", &_数据文本),
("扩展中枢序列_数据文本", &_数据文本),
("扩展中枢序列_线段_数据文本", &_线段_数据文本),
("线段_中枢序列_数据文本", &线_中枢序列_数据文本),
("扩展中枢序列_扩展线段_数据文本", &_扩展线段_数据文本),
];
for (, ) in & {
let = .join(format!("{}.txt", ));
let = .join("\n") + "\n";
if let Err(e) = std::fs::write(&, &) {
eprintln!("写入文件失败: {} -> {}", .display(), e);
}
}
println!("全部数据拆分保存完成,目录:{}", .display());
}
/// 读取数据文件 — 从 .nb 文件加载数据
pub fn (: &str, : Option<>) -> Result<Self, String> {
let = .unwrap_or_default();
// Parse filename: btcusd-300-1631772074-1632222374.nb
let path = std::path::Path::new();
let name = path
.file_stem()
.and_then(|n| n.to_str())
.ok_or("invalid filename")?;
let parts: Vec<&str> = name.split('-').collect();
if parts.len() < 4 {
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 mut = Self::new(, , );
let data = std::fs::read().map_err(|e| format!("read file: {}", e))?;
let size = 48; // 6 × 8 bytes (big-endian double)
for i in 0..data.len() / size {
let offset = i * size;
if let Some(k线) = K线::from_bytes(&data[offset..offset + size], , "nb") {
.K线(k线);
}
}
Ok()
}
}
+115
View File
@@ -0,0 +1,115 @@
use crate::kline::bar::K线;
use std::collections::HashMap;
/// K线合成器 — 将小周期K线合成为大周期K线
pub struct K线合成器 {
pub : String,
pub : Vec<i64>,
pub K线: HashMap<i64, Option<K线>>,
pub K线列表: HashMap<i64, Vec<K线>>,
}
impl K线合成器 {
pub fn new(: String, : Vec<i64>) -> Self {
let mut = ;
.sort();
let mut K线 = HashMap::new();
let mut K线列表 = HashMap::new();
for & in & {
K线.insert(, None);
K线列表.insert(, Vec::new());
}
Self {
,
,
K线,
K线列表,
}
}
/// 投喂K线 — 输入最小周期K线,合成为所有目标周期
pub fn K线(&mut self, K: K线) {
let = self..clone();
for in {
self._处理单个周期(, &K);
}
}
fn _处理单个周期(&mut self, : i64, K: &K线) {
let = self._对齐时间戳(K., );
let K线 = self.K线.get(&).unwrap().clone();
if K线.is_none() {
// 创建新K线
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线);
} else {
// 完成当前K线,创建新K线
self._完成K线();
let K线 = self._创建新K线(, , K);
*self.K线.get_mut(&).unwrap() = Some(K线);
}
}
fn _对齐时间戳(&self, : i64, : i64) -> i64 {
if == 0 {
return ;
}
( / ) *
}
fn _创建新K线(&self, : i64, : i64, K: &K线) -> K线 {
let = self
.K线列表
.get(&)
.and_then(|list| list.last())
.map(|k| k. + 1)
.unwrap_or(0);
K线::K(
&self.,
,
K.,
K.,
K.,
K.,
K.,
,
,
)
}
fn _更新K线(&self, 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);
self.K线列表.get_mut(&).unwrap().push(k线);
*self.K线.get_mut(&).unwrap() = None;
}
}
/// 获取指定周期当前正在合成的K线
pub fn K线(&self, : i64) -> Option<&K线> {
self.K线.get(&).and_then(|k| k.as_ref())
}
}
+338
View File
@@ -0,0 +1,338 @@
use serde::{Deserialize, Serialize};
fn is_infinite_f64(v: &f64) -> bool {
v.is_infinite()
}
/// 缠论配置 —— 控制所有分析阶段的行为
///
/// 所有字段带默认值,使用 `#[serde(default)]` 实现缺失字段容错
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct {
// ---- 基础 ----
pub : String,
// ---- 缠K ----
pub K合并替换: bool,
// ---- 笔 ----
pub : i64,
pub : bool,
pub : bool,
pub _包括右: bool,
pub K线包含整笔: bool,
pub : bool,
pub : bool,
pub _原始数量: i64,
// ---- 线段 ----
pub 线_非缺口下穿刺: bool,
pub 线_特征序列忽视老阴老阳: bool,
pub 线_缺口后紧急修正: bool,
pub 线_修正: bool,
pub 线: bool,
pub 线_当下分析: bool,
// ---- 分析开关 ----
pub : bool,
pub 线: bool,
pub 线: bool,
pub : bool,
pub 线: bool,
// ---- 终止 ----
pub : String,
// ---- 指标 ----
pub : bool,
pub : String,
// ---- MACD ----
pub 线_快线周期: i64,
pub 线_慢线周期: i64,
pub 线_信号周期: i64,
// ---- RSI ----
pub _周期: i64,
pub _移动平均线周期: i64,
pub _超买阈值: f64,
pub _超卖阈值: f64,
// ---- KDJ ----
pub _RSV周期: i64,
pub _K值平滑周期: i64,
pub _D值平滑周期: i64,
pub _超买阈值: f64,
pub _超卖阈值: f64,
// ---- 推送/显示 ----
pub : bool,
pub K线: bool,
pub : bool,
pub 线: bool,
pub : bool,
// ---- 图表展示细分 ----
pub _笔: bool,
pub _线段: bool,
pub _扩展线段: bool,
pub _扩展线段_线段: bool,
pub _线段_线段: bool,
pub _中枢_笔: bool,
pub _中枢_线段: bool,
pub _中枢_扩展线段: bool,
pub _中枢_扩展线段_线段: bool,
pub _中枢_线段_线段: bool,
pub _中枢_线段内部: bool,
// ---- 买卖点 ----
pub : i64,
pub : bool,
pub MACD柱强相关: bool,
pub : f64,
pub _指标模式: String,
pub _指标匹配_MACD: bool,
pub _指标匹配_KDJ: bool,
pub _指标匹配_RSI: bool,
#[serde(skip_serializing_if = "is_infinite_f64")]
pub _背离率: f64,
pub _T2_回调阈值: f64,
pub _T2S_最大层级: i64,
pub _峰值条件: bool,
pub _计算方式: String,
pub _计算线段BSP1: bool,
pub _处理BSP2: bool,
pub _计算线段BSP3: bool,
pub _依赖T1: bool,
pub _中枢来源: String,
pub _调试输出: bool,
// ---- 背驰 ----
pub 线_MACD: bool,
pub 线_斜率: bool,
pub 线_测度: bool,
pub 线_模式: String,
// ---- 文件 ----
pub : String,
}
impl Default for {
fn default() -> Self {
Self {
: "bar".into(),
K合并替换: false,
: 5,
: false,
: false,
_包括右: false,
K线包含整笔: false,
: false,
: false,
_原始数量: 3,
线_非缺口下穿刺: false,
线_特征序列忽视老阴老阳: false,
线_缺口后紧急修正: true,
线_修正: false,
线: true,
线_当下分析: false,
: true,
线: true,
线: true,
: true,
线: true,
: String::new(),
: true,
: "".into(),
线_快线周期: 13,
线_慢线周期: 31,
线_信号周期: 11,
_周期: 13,
_移动平均线周期: 13,
_超买阈值: 75.0,
_超卖阈值: 25.0,
_RSV周期: 13,
_K值平滑周期: 5,
_D值平滑周期: 5,
_超买阈值: 80.0,
_超卖阈值: 20.0,
: true,
K线: true,
: true,
线: true,
: true,
_笔: true,
_线段: true,
_扩展线段: true,
_扩展线段_线段: true,
_线段_线段: true,
_中枢_笔: true,
_中枢_线段: true,
_中枢_扩展线段: true,
_中枢_扩展线段_线段: true,
_中枢_线段_线段: true,
_中枢_线段内部: true,
: 1,
: false,
MACD柱强相关: false,
: 0.01,
_指标模式: "配置".into(),
_指标匹配_MACD: true,
_指标匹配_KDJ: true,
_指标匹配_RSI: true,
_背离率: f64::INFINITY,
_T2_回调阈值: 1.0,
_T2S_最大层级: 3,
_峰值条件: false,
_计算方式: "".into(),
_计算线段BSP1: true,
_处理BSP2: true,
_计算线段BSP3: true,
_依赖T1: true,
_中枢来源: "".into(),
_调试输出: false,
线_MACD: true,
线_斜率: true,
线_测度: true,
线_模式: "相对".into(),
: "./templates/last.nb".into(),
}
}
}
impl {
pub fn to_json(&self) -> String {
serde_json::to_string_pretty(self).unwrap_or_default()
}
pub fn from_json(json_str: &str) -> Result<Self, serde_json::Error> {
serde_json::from_str(json_str)
}
pub fn (&self, path: &str) -> std::io::Result<()> {
std::fs::write(path, self.to_json())
}
pub fn (path: &str) -> Result<Self, Box<dyn std::error::Error>> {
let content = std::fs::read_to_string(path)?;
let config = Self::from_json(&content)?;
Ok(config)
}
/// 返回一个关闭所有推送/显示的新配置
pub fn (&self) -> Self {
Self {
: false,
K线: false,
: false,
线: false,
: false,
_笔: false,
_线段: false,
_扩展线段: false,
_扩展线段_线段: false,
_线段_线段: false,
_中枢_笔: false,
_中枢_线段: false,
_中枢_扩展线段: false,
_中枢_扩展线段_线段: false,
_中枢_线段_线段: false,
_中枢_线段内部: false,
..self.clone()
}
}
/// 按序号重组字典 — 兼容旧版配置的复合key格式
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();
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());
}
}
}
for (num, fields) in groups {
let mut config = .clone();
if let Ok(partial) = serde_json::from_value::<>(serde_json::Value::Object(fields)) {
// merge partial into config (override matching fields)
config = partial;
}
result.push((num, config));
}
}
result
}
/// 对比两个配置,返回差异字段
pub fn (&self, other: &Self) -> Vec<String> {
let mut diffs = Vec::new();
let self_json = serde_json::to_value(self).unwrap();
let other_json = serde_json::to_value(other).unwrap();
if let (serde_json::Value::Object(self_map), serde_json::Value::Object(other_map)) =
(&self_json, &other_json)
{
for (key, self_val) in self_map {
if let Some(other_val) = other_map.get(key) {
if self_val != other_val {
diffs.push(key.clone());
}
}
}
}
diffs
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config_roundtrip() {
let config = ::default();
let json = config.to_json();
let parsed: = ::from_json(&json).unwrap();
let json2 = parsed.to_json();
assert_eq!(json, json2);
}
#[test]
fn test_default_values() {
let config = ::default();
assert_eq!(config., "bar");
assert_eq!(config., 5);
assert!(config._背离率.is_infinite());
assert_eq!(config., "");
}
#[test]
fn test_partial_deserialize() {
let json = r#"{"标识": "custom", "笔内元素数量": 7}"#;
let config: = serde_json::from_str(json).unwrap();
assert_eq!(config., "custom");
assert_eq!(config., 7);
// 未指定字段使用默认值
assert_eq!(config., 1);
}
#[test]
fn test_不推送() {
let config = ::default();
let muted = config.();
assert!(!muted.K线);
assert!(!muted.);
assert!(!muted.);
// 其他字段不变
assert_eq!(muted., 5);
}
}
+204
View File
@@ -0,0 +1,204 @@
use serde::{Deserialize, Serialize};
/// 随机指标 (KDJ)
///
/// 使用滑动窗口 + 逐值平滑进行增量计算
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct {
pub : i64,
pub : f64,
pub : f64,
pub : f64,
pub N: i64,
pub M1: i64,
pub M2: i64,
pub : f64,
pub : f64,
pub RSV: Option<f64>,
pub K: Option<f64>,
pub D: Option<f64>,
pub J: Option<f64>,
pub : Vec<f64>,
pub : Vec<f64>,
pub RSV: Option<f64>,
pub K: Option<f64>,
pub D: Option<f64>,
}
impl Default for {
fn default() -> Self {
Self {
: 0,
: 0.0,
: 0.0,
: 0.0,
N: 9,
M1: 3,
M2: 3,
: 80.0,
: 20.0,
RSV: None,
K: None,
D: None,
J: None,
: Vec::new(),
: Vec::new(),
RSV: None,
K: None,
D: None,
}
}
}
impl {
/// 首次计算 KDJ(无历史数据时)
pub fn (
: f64,
: f64,
: f64,
: i64,
N: i64,
M1: i64,
M2: i64,
: f64,
: f64,
) -> Self {
Self {
: ,
: ,
: ,
: ,
N,
M1,
M2,
,
,
RSV: None,
K: None,
D: None,
J: None,
: vec![],
: vec![],
RSV: None,
K: None,
D: None,
}
}
/// 基于前一个 KDJ 增量计算当前 KDJ
pub fn (KDJ: &Self, : f64, : f64, : f64, : i64) -> Self {
let N = KDJ.N;
let M1 = KDJ.M1;
let M2 = KDJ.M2;
let = KDJ.;
let = KDJ.;
// 更新历史最高价队列
let mut = KDJ..clone();
.push();
if .len() > N as usize {
.remove(0);
}
// 更新历史最低价队列
let mut = KDJ..clone();
.push();
if .len() > N as usize {
.remove(0);
}
// RSV
let RSV = if .len() == N as usize && .len() == N as usize {
let highest = .iter().cloned().fold(f64::NEG_INFINITY, f64::max);
let lowest = .iter().cloned().fold(f64::INFINITY, f64::min);
if (highest - lowest).abs() > f64::EPSILON {
Some(( - lowest) / (highest - lowest) * 100.0)
} else {
Some(50.0)
}
} else {
None
};
// K值
let K = match RSV {
Some(rsv) => match KDJ.K {
None => Some(rsv),
Some(prev_k) => Some((prev_k * (M1 - 1) as f64 + rsv) / M1 as f64),
},
None => KDJ.K,
};
// D值
let D = match K {
Some(k) => match KDJ.D {
None => Some(k),
Some(prev_d) => Some((prev_d * (M2 - 1) as f64 + k) / M2 as f64),
},
None => KDJ.D,
};
// J值
let J = match (K, D) {
(Some(k), Some(d)) => Some(3.0 * k - 2.0 * d),
_ => None,
};
Self {
: ,
: ,
: ,
: ,
N,
M1,
M2,
,
,
RSV,
K,
D,
J,
: ,
: ,
RSV: RSV,
K: K,
D: D,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_first_calc() {
let kdj = ::(110.0, 90.0, 100.0, 1000, 9, 3, 3, 80.0, 20.0);
assert_eq!(kdj., vec![110.0]);
assert_eq!(kdj., vec![90.0]);
assert_eq!(kdj.K, None);
}
#[test]
fn test_incremental_after_n_bars() {
let mut kdj = ::(110.0, 90.0, 100.0, 1000, 5, 3, 3, 80.0, 20.0);
// 喂入足够数据填充窗口
let data = [
(112.0, 91.0, 105.0),
(115.0, 93.0, 110.0),
(113.0, 95.0, 108.0),
(116.0, 98.0, 112.0),
(118.0, 100.0, 115.0),
];
for (i, (, , )) in data.iter().enumerate() {
kdj = ::(&kdj, *, *, *, 1001 + i as i64);
}
// 窗口填满后 KDJ 应有值
assert!(kdj.K.is_some());
assert!(kdj.D.is_some());
assert!(kdj.J.is_some());
}
}
+149
View File
@@ -0,0 +1,149 @@
use serde::{Deserialize, Serialize};
/// 平滑异同移动平均线 (MACD)
///
/// 使用 EMA 递推算法进行增量计算
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct 线 {
pub : i64,
pub : f64,
pub 线: i64,
pub 线: i64,
pub : i64,
pub DIF: Option<f64>,
pub DEA: Option<f64>,
#[serde(rename = "MACD柱")]
#[serde(default)]
pub MACD柱: f64,
pub 线EMA: Option<f64>,
pub 线EMA: Option<f64>,
pub DEA_EMA: Option<f64>,
}
impl Default for 线 {
fn default() -> Self {
Self {
: 0,
: 0.0,
线: 12,
线: 26,
: 9,
DIF: None,
DEA: None,
MACD柱: 0.0,
线EMA: None,
线EMA: None,
DEA_EMA: None,
}
}
}
fn (: i64) -> f64 {
2.0 / ( as f64 + 1.0)
}
impl 线 {
/// 首次计算 MACD 指标(无历史数据时使用)
pub fn (: f64, : i64, 线: i64, 线: i64, : i64) -> Self {
let 线EMA = ;
let 线EMA = ;
let DIF = 线EMA - 线EMA;
let DEA_EMA = DIF;
let MACD柱 = DIF - DEA_EMA;
Self {
: ,
: ,
线,
线,
,
DIF: Some(DIF),
DEA: Some(DEA_EMA),
MACD柱,
线EMA: Some(线EMA),
线EMA: Some(线EMA),
DEA_EMA: Some(DEA_EMA),
}
}
/// 基于前一个 MACD 指标增量计算当前 MACD
pub fn (MACD: &Self, : f64, : i64) -> Self {
// 快线 EMA
let 线EMA = match MACD.线EMA {
Some(prev) => {
* (MACD.线)
+ prev * ((MACD.线 - 1) as f64 / (MACD.线 + 1) as f64)
}
None => ,
};
// 慢线 EMA
let 线EMA = match MACD.线EMA {
Some(prev) => {
* (MACD.线)
+ prev * ((MACD.线 - 1) as f64 / (MACD.线 + 1) as f64)
}
None => ,
};
// DIF
let DIF = 线EMA - 线EMA;
// DEA_EMA
let DEA_EMA = match MACD.DEA_EMA {
Some(prev) => {
DIF * (MACD.)
+ prev * ((MACD. - 1) as f64 / (MACD. + 1) as f64)
}
None => DIF,
};
// MACD 柱 (注意: Python 版没有 ×2)
let MACD柱 = DIF - DEA_EMA;
Self {
: ,
: ,
线: MACD.线,
线: MACD.线,
: MACD.,
DIF: Some(DIF),
DEA: Some(DEA_EMA),
MACD柱,
线EMA: Some(线EMA),
线EMA: Some(线EMA),
DEA_EMA: Some(DEA_EMA),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_first_calc() {
let macd = 线::(100.0, 1000, 12, 26, 9);
assert_eq!(macd.DIF, Some(0.0));
assert_eq!(macd.MACD柱, 0.0);
assert_eq!(macd.线EMA, Some(100.0));
assert_eq!(macd.线EMA, Some(100.0));
}
#[test]
fn test_incremental_calc() {
let first = 线::(100.0, 1000, 12, 26, 9);
// 价格上升
let second = 线::(&first, 102.0, 1001);
assert!(second.DIF.unwrap() > 0.0);
// 快线EMA 应该比慢线EMA 变化更快
assert!(second.线EMA.unwrap() > second.线EMA.unwrap());
// 价格下降
let third = 线::(&second, 98.0, 1002);
// DIF 应该变小
assert!(third.DIF.unwrap() < second.DIF.unwrap());
}
}
+21
View File
@@ -0,0 +1,21 @@
pub mod macd;
pub mod kdj;
pub mod rsi;
pub use macd::线;
pub use kdj::;
pub use rsi::;
/// K线取值 —— 根据计算方式从K线提取对应的价格
pub fn K线取值(: f64, : f64, : f64, : f64, : &str) -> f64 {
match {
"" => ,
"" => ,
"" => ,
"" => ,
"高低均值" => ( + ) / 2.0,
"高低收均值" => ( + + ) / 3.0,
"开高低收均值" => ( + + + ) / 4.0,
_ => ,
}
}
+180
View File
@@ -0,0 +1,180 @@
use serde::{Deserialize, Serialize};
/// 相对强弱指数 (RSI)
///
/// 使用 Wilder 平滑(RMA)进行增量计算
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct {
pub : i64,
pub : f64,
pub : i64,
pub : f64,
pub : f64,
pub RSI_SMA周期: Option<i64>,
pub RSI: Option<f64>,
pub : Option<f64>,
pub : Option<f64>,
pub : f64,
pub : f64,
pub : f64,
pub RSI_SMA: Option<f64>,
pub RSI历史队列: Vec<f64>,
}
impl Default for {
fn default() -> Self {
Self {
: 0,
: 0.0,
: 14,
: 70.0,
: 30.0,
RSI_SMA周期: None,
RSI: None,
: None,
: None,
: 0.0,
: 0.0,
: 0.0,
RSI_SMA: None,
RSI历史队列: Vec::new(),
}
}
}
impl {
/// 首次计算 RSI(历史数据不足时)
pub fn (
: f64,
: i64,
: i64,
: f64,
: f64,
RSI_SMA周期: Option<i64>,
) -> Self {
Self {
: ,
: ,
,
,
,
RSI_SMA周期,
RSI: None,
: Some(0.0),
: Some(0.0),
: 0.0,
: 0.0,
: 1.0 / as f64,
RSI_SMA: None,
RSI历史队列: Vec::new(),
}
}
/// 基于前一个 RSI 增量计算当前 RSI
pub fn (RSI: &Self, : f64, : i64) -> Self {
let = RSI.;
let = RSI.;
let = RSI.;
let RSI_SMA周期 = RSI.RSI_SMA周期;
let = 1.0 / as f64;
// 价格变化
let = - RSI.;
let = .max(0.0);
let = (-).max(0.0);
// Wilder 平滑
let (, ) = match (RSI., RSI.) {
(Some(prev_up), Some(prev_down)) => {
let avg_up = prev_up * (1.0 - ) + * ;
let avg_down = prev_down * (1.0 - ) + * ;
(avg_up, avg_down)
}
_ => (, ),
};
// RSI
let RSI = if == 0.0 {
if > 0.0 {
100.0
} else {
50.0
}
} else {
let RS = / ;
100.0 - (100.0 / (1.0 + RS))
};
// RSI_SMA
let (RSI_SMA, RSI历史队列) = match RSI_SMA周期 {
Some(sma周期) if sma周期 > 0 => {
let mut = RSI.RSI历史队列.clone();
.push(RSI);
if .len() > sma周期 as usize {
.remove(0);
}
let sma = if .is_empty() {
None
} else {
Some(.iter().sum::<f64>() / .len() as f64)
};
(sma, )
}
_ => (None, Vec::new()),
};
Self {
: ,
: ,
,
,
,
RSI_SMA周期,
RSI: Some(RSI),
: Some(),
: Some(),
: ,
: ,
,
RSI_SMA,
RSI历史队列,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_first_calc() {
let rsi = ::(100.0, 1000, 14, 70.0, 30.0, None);
assert_eq!(rsi.RSI, None);
assert_eq!(rsi., 1.0 / 14.0);
}
#[test]
fn test_incremental_calc() {
let first = ::(100.0, 1000, 14, 70.0, 30.0, None);
let second = ::(&first, 102.0, 1001);
// 价格上涨 → RSI > 50
assert!(second.RSI.unwrap() > 50.0);
let third = ::(&second, 98.0, 1002);
// 价格低于之前 → RSI 下降
assert!(third.RSI.unwrap() < second.RSI.unwrap());
}
#[test]
fn test_rsi_sma() {
let mut rsi = ::(100.0, 1000, 14, 70.0, 30.0, Some(5));
// 喂入多根K线来积累RSI历史队列
let prices = [102.0, 103.0, 101.0, 104.0, 105.0, 103.0, 106.0];
for (i, price) in prices.iter().enumerate() {
rsi = ::(&rsi, *price, 1001 + i as i64);
}
// SMA 应该已被计算(队列够长)
assert!(rsi.RSI_SMA.is_some());
}
}
+244
View File
@@ -0,0 +1,244 @@
use crate::indicators::{线, , };
use crate::types::;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::io::Write;
use std::rc::Rc;
/// 原始K线 (OHLCV + 指标)
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct K线 {
pub : String,
pub : i64,
pub : i64,
pub : i64,
pub : f64,
pub : f64,
pub : f64,
pub : f64,
pub : f64,
pub macd: Option<线>,
pub rsi: Option<>,
pub kdj: Option<>,
}
impl Default for K线 {
fn default() -> Self {
Self {
: "bar".into(),
: 0,
: 60,
: 0,
: 0.0,
: 0.0,
: 0.0,
: 0.0,
: 0.0,
macd: None,
rsi: None,
kdj: None,
}
}
}
impl K线 {
/// 方向:阳(收盘 > 开盘)为向上,否则向下
pub fn (&self) -> {
if self. < self. {
::
} else {
::
}
}
/// 序列化为大端字节序 48 字节
/// 格式: >6d (时间戳, 开盘价, 高, 低, 收盘价, 成交量)
pub fn to_bytes(&self) -> [u8; 48] {
let mut buf = [0u8; 48];
{
let mut writer = &mut buf[..];
writer.write_f64::<BigEndian>(self. as f64).unwrap();
writer.write_f64::<BigEndian>(self.).unwrap();
writer.write_f64::<BigEndian>(self.).unwrap();
writer.write_f64::<BigEndian>(self.).unwrap();
writer.write_f64::<BigEndian>(self.).unwrap();
writer.write_f64::<BigEndian>(self.).unwrap();
}
buf
}
/// 从大端字节序反序列化
pub fn from_bytes(: &[u8], : i64, : &str) -> Option<Self> {
if .len() < 48 {
return None;
}
let mut reader = &[..48];
let = reader.read_f64::<BigEndian>().ok()? as i64;
let = reader.read_f64::<BigEndian>().ok()?;
let = reader.read_f64::<BigEndian>().ok()?;
let = reader.read_f64::<BigEndian>().ok()?;
let = reader.read_f64::<BigEndian>().ok()?;
let = reader.read_f64::<BigEndian>().ok()?;
Some(Self {
,
,
,
,
,
,
,
: .to_string(),
: 0,
..Default::default()
})
}
/// 读取 .nb 文件中的所有 K线
pub fn (: &[u8], : i64, : &str) -> Option<Self> {
Self::from_bytes(, , )
}
/// 创建普通K线
pub fn K(
: &str,
: i64,
: f64,
: f64,
: f64,
: f64,
: f64,
: i64,
: i64,
) -> Self {
Self {
: .to_string(),
,
,
,
: ,
: ,
,
,
,
macd: None,
rsi: None,
kdj: None,
}
}
/// 保存K线序列到 DAT 文件
pub fn DAT文件(: &str, K线序列: &[&Self]) -> std::io::Result<()> {
let mut f = std::fs::File::create()?;
for k in K线序列 {
f.write_all(&k.to_bytes())?;
}
Ok(())
}
/// 获取两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 = &K线序列[_idx..=_idx];
let mut = 0.0f64;
let mut = 0.0f64;
for k in {
if let Some(ref macd) = k.macd {
let hist = macd.MACD柱;
if hist >= 0.0 {
+= hist;
} else {
+= hist;
}
}
}
let = + ;
let mut map = HashMap::new();
map.insert("".into(), );
map.insert("".into(), );
map.insert("".into(), );
map.insert("".into(), + .abs());
map
}
/// 截取K线序列中从始到终的片段
pub fn <'a>(: &'a [Self], : &'a Self, : &'a Self) -> Option<&'a [Self]> {
let _idx = .iter().position(|k| std::ptr::eq(k, ))?;
let _idx = .iter().position(|k| std::ptr::eq(k, ))?;
Some(&[_idx..=_idx])
}
/// 截取Rc<K线>序列中从始到终的片段
pub fn rc(: &[Rc<Self>], : &Rc<Self>, : &Rc<Self>) -> Vec<Rc<Self>> {
let _ptr = Rc::as_ptr();
let _ptr = Rc::as_ptr();
let _idx = .iter().position(|k| Rc::as_ptr(k) == _ptr);
let _idx = .iter().position(|k| Rc::as_ptr(k) == _ptr);
match (_idx, _idx) {
(Some(s), Some(e)) => [s..=e].to_vec(),
_ => Vec::new(),
}
}
}
impl std::fmt::Display for K线 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use crate::utils::format_f64_g;
write!(
f,
"{}<{}, {}, {}, {}, {}, {}, {}, {}>",
self.,
self.,
self.,
self.(),
self.,
format_f64_g(self.),
format_f64_g(self.),
format_f64_g(self.),
format_f64_g(self.)
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_方向() {
let = K线::K("test", 1000, 100.0, 110.0, 95.0, 105.0, 1000.0, 0, 60);
assert_eq!(.(), ::);
let = K线::K("test", 1000, 105.0, 110.0, 95.0, 100.0, 1000.0, 0, 60);
assert_eq!(.(), ::);
}
#[test]
fn test_serialization_roundtrip() {
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();
assert_eq!(restored., 1600000000);
assert!((restored. - 100.5).abs() < 0.01);
assert!((restored. - 110.2).abs() < 0.01);
assert!((restored. - 95.3).abs() < 0.01);
assert!((restored. - 105.7).abs() < 0.01);
assert!((restored. - 5000.0).abs() < 0.01);
}
#[test]
fn test_获取MACD_empty() {
let k1 = K线::default();
let k2 = K线::default();
let seq = vec![&k1, &k2];
let result = K线::MACD(&seq, &k1, &k2);
assert_eq!(result.get(""), Some(&0.0));
assert_eq!(result.get(""), Some(&0.0));
assert_eq!(result.get(""), Some(&0.0));
}
}
+601
View File
@@ -0,0 +1,601 @@
use crate::config::;
use crate::indicators::{K线取值, 线, , };
use crate::kline::bar::K线;
use crate::structure::fractal_obj::;
use crate::types::;
use crate::types::;
use std::rc::Rc;
/// 缠论K线 — 经包含处理过后的K线
#[derive(Debug, Clone)]
pub struct K线 {
pub : i64,
pub : i64,
pub : f64,
pub : f64,
pub : ,
pub : Option<>,
pub : i64,
pub : String,
pub : f64,
pub : i64,
pub : i64,
pub K线: Rc<K线>,
pub : Option<()>, // 占位,后续替换为实际类型
}
impl std::fmt::Display for K线 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use crate::utils::format_f64_g;
write!(
f,
"{}<{}, {}, {}, {}, {}, {}, {}>",
self.,
self.,
self..map_or("None".to_string(), |fx| fx.to_string()),
self.,
self.,
self.,
format_f64_g(self.),
format_f64_g(self.)
)
}
}
impl K线 {
/// 创建镜像(浅拷贝 Rc 引用)
pub fn (&self) -> Self {
Self {
: self.,
: self.,
: self.,
: self.,
: self.,
: self.,
: self.,
: self..clone(),
: self.,
: self.,
: self.,
K线: Rc::clone(&self.K线),
: None,
}
}
/// 与MACD柱子匹配 — 底分型时MACD柱应<0, 顶分型时>0
pub fn MACD柱子匹配(&self) -> bool {
match self. {
Some(::) | Some(::) => {
if let Some(ref macd) = self.K线.macd {
macd.MACD柱 < 0.0
} else {
false
}
}
Some(::) | Some(::) => {
if let Some(ref macd) = self.K线.macd {
macd.MACD柱 > 0.0
} else {
false
}
}
_ => false,
}
}
/// 与RSI匹配 — 底分型时RSI应低于SMA, 顶分型时高于SMA
pub fn RSI匹配(&self) -> bool {
match self. {
Some(::) | Some(::) => {
if let Some(ref rsi) = self.K线.rsi {
match (rsi.RSI, rsi.RSI_SMA) {
(Some(r), Some(sma)) => r < sma,
_ => false,
}
} else {
false
}
}
Some(::) | Some(::) => {
if let Some(ref rsi) = self.K线.rsi {
match (rsi.RSI, rsi.RSI_SMA) {
(Some(r), Some(sma)) => r > sma,
_ => false,
}
} else {
false
}
}
_ => false,
}
}
/// 与KDJ匹配 — 底分型时K应低于D(死叉后), 顶分型时K应高于D(金叉后)
pub fn KDJ匹配(&self) -> bool {
match self. {
Some(::) | Some(::) => {
if let Some(ref kdj) = self.K线.kdj {
match (kdj.K, kdj.D) {
(Some(k), Some(d)) => k < d,
_ => false,
}
} else {
false
}
}
Some(::) | Some(::) => {
if let Some(ref kdj) = self.K线.kdj {
match (kdj.K, kdj.D) {
(Some(k), Some(d)) => k > d,
_ => false,
}
} else {
false
}
}
_ => false,
}
}
/// 时间戳对齐 — 从基线序列中找匹配的时间戳
pub fn (线: &[Rc<K线>], k线: &K线) -> i64 {
if let Some() = 线.first() {
for k in 线.iter().rev() {
if . < k线. {
if k线. <= k. && k. <= k线. + k线. {
if (k线. - k.).abs() < f64::EPSILON {
return k.;
}
}
} else if k. <= k线. && k线. <= k. + k. {
if (k线. - k.).abs() < f64::EPSILON {
return k.;
}
}
}
}
k线.
}
/// 创建缠K
pub fn K(
: i64,
: f64,
: f64,
: ,
: Option<>,
: i64,
k: Rc<K线>,
: Option<&K线>,
) -> Self {
assert!( >= , "缠K高必须>=低: 高={高}, 低={低}");
let = k.;
let = k..clone();
let mut = Self {
: 0,
,
,
,
,
: ,
,
,
: ,
: ,
: ,
K线: k,
: None,
};
if let Some() = {
. = . + 1;
let = ::(., ., ., .);
if .() {
panic!(
"创建缠K 包含关系: {:?}\n 之前: {}\n 当前: {}",
, ,
);
}
}
}
/// 兼并(合并)处理 — 缠论包含处理的核心算法
///
/// 返回 (新缠K, 模式) — 模式: "添加"/"替换"/None
pub fn (
K: Option<&K线>,
K: &K线,
K: &K线,
: &,
) -> (Option<Rc<K线>>, Option<String>) {
let = ::(K., K., K., K.);
// 无包含关系 — 创建新元素追加
if !.() {
let = if .() {
Some(::)
} else {
Some(::)
};
let mut K = Self::K(
K.,
K.,
K.,
K.(),
,
K.,
Rc::new(K.clone()),
Some(K),
);
K. = K. + 1;
return (Some(Rc::new(K)), Some("添加".into()));
}
// 包含关系 — 合并到当前缠K
// 取值方向:向下 → 取低低、高高(即全部取最低);向上 → 取高高、低低(即全部取最高)
// 方向由之前缠K与当前缠K的关系决定
let : fn(f64, f64) -> f64 = if let Some() = K {
if ::(., ., K., K.).() {
f64::min
} else {
f64::max
}
} else {
f64::max // 默认向上取max
};
let mut = K.();
// 逆序包含时更新时间和标的K线
if != :: {
. = K.;
.K线 = Rc::new(K.clone());
}
. = (., K.);
. = (., K.);
. = K.;
. = K.();
if let Some() = K {
. = . + 1;
}
if .K合并替换 {
(Some(Rc::new()), Some("替换".into()))
} else {
// 兼并:返回新值让调用方决定是原地修改还是替换
(Some(Rc::new()), Some("兼并".into()))
}
}
/// 完整的缠K分析 — 普K → 缠K + 分型
///
/// 返回 (状态, 形态)
pub fn (
K线: &mut K线,
K序列: &mut Vec<Rc<K线>>,
K序列: &mut Vec<Rc<K线>>,
: &,
) -> (String, Option<Rc<>>) {
K线. = ..clone();
// ---- 阶段1: 普K序列管理 + 指标增量计算 ----
if K序列.is_empty() {
if . {
K线.macd = Some(线::(
K线取值(K线., K线., K线., K线., &.),
K线.,
.线_快线周期,
.线_慢线周期,
.线_信号周期,
));
K线.rsi = Some(::(
K线取值(K线., K线., K线., K线., &.),
K线.,
._周期,
._超买阈值,
._超卖阈值,
Some(._移动平均线周期),
));
K线.kdj = Some(::(
K线.,
K线.,
K线.,
K线.,
._RSV周期,
._K值平滑周期,
._D值平滑周期,
._超买阈值,
._超卖阈值,
));
}
let K线_rc = Rc::new(K线.clone());
K序列.push(K线_rc);
} else {
let K = K序列.last().unwrap();
if K. == K线. {
// 同时间戳更新
K线. = K.;
if . {
if K序列.len() >= 2 {
if let Some(ref prev_macd) = K序列[K序列.len() - 2].macd {
K线.macd = Some(线::(
prev_macd,
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线.,
));
}
if let Some(ref prev_kdj) = K序列[K序列.len() - 2].kdj {
K线.kdj = Some(::(
prev_kdj,
K线.,
K线.,
K线.,
K线.,
));
}
}
}
K序列.pop();
K序列.push(Rc::new(K线.clone()));
} else {
if K. > K线. {
panic!("时序错误: 之前={}, 当前={}", K., K线.);
}
K线. = K. + 1;
if . {
if let Some(ref prev_macd) = K.macd {
K线.macd = Some(线::(
prev_macd,
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线.,
));
}
if let Some(ref prev_kdj) = K.kdj {
K线.kdj = Some(::(
prev_kdj,
K线.,
K线.,
K线.,
K线.,
));
}
}
K序列.push(Rc::new(K线.clone()));
}
}
// ---- 阶段2: 缠K合并 ----
let K: Option<&K线> = if K序列.len() >= 2 {
Some(&K序列[K序列.len() - 2])
} else {
None
};
let : String;
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线, );
if let Some(k) = K {
if let Some(ref m) = {
if m == "添加" {
K序列.push(k);
= "创建".into();
} else if m == "替换" {
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();
}
} else {
= "兼并".into();
}
} else {
let K = Self::K(
K线.,
K线.,
K线.,
K线.(),
None,
K线.,
Rc::new(K线.clone()),
None,
);
K序列.push(Rc::new(K));
= "新建".into();
}
// ---- 阶段3: 分型识别 ----
if K序列.len() < 3 {
return (, None);
}
let idx = K序列.len();
let = Rc::clone(&K序列[idx - 3]);
let = Rc::clone(&K序列[idx - 2]);
let = Rc::clone(&K序列[idx - 1]);
let = ::(&*, &*, &*, false, false);
// 需要通过 Rc::get_mut 或 RefCell 修改 中.分型
// 由于使用 Rc,中是不可变的。这里采用创建新 Rc 替换的方式。
// 但这是在 Vec 内部修改,需要使用 Rc::make_mut 或重新构建
if let Some() = {
// 只在分型未设置或需要更新时才修改缠K,以保持 Rc 指针不变
let = K序列[idx - 2].;
let = .is_none() || != Some();
if {
let _mut = Rc::make_mut(&mut K序列[idx - 2]);
_mut. = Some();
match {
:: => {
_mut. = _mut.;
let = K序列[idx - 1].;
if .is_none() {
let _mut = Rc::make_mut(&mut K序列[idx - 1]);
_mut. = _mut.;
_mut. = Some(::);
}
}
:: => {
_mut. = _mut.;
let = K序列[idx - 1].;
if .is_none() {
let _mut = Rc::make_mut(&mut K序列[idx - 1]);
_mut. = _mut.;
_mut. = Some(::);
}
}
:: => {
_mut. = _mut.;
let = K序列[idx - 1].;
if .is_none() {
let _mut = Rc::make_mut(&mut K序列[idx - 1]);
_mut. = _mut.;
_mut. = Some(::);
}
}
:: => {
_mut. = _mut.;
let = K序列[idx - 1].;
if .is_none() {
let _mut = Rc::make_mut(&mut K序列[idx - 1]);
_mut. = _mut.;
_mut. = Some(::);
}
}
:: => {}
}
}
let = if matches!(, :: | ::) {
// 三连方向不算完整分型 — 只有中+右
Rc::new(::new(
None,
Rc::clone(&K序列[idx - 2]),
None,
))
} else {
Rc::new(::new(
Some(Rc::clone(&K序列[idx - 3])),
Rc::clone(&K序列[idx - 2]),
Some(Rc::clone(&K序列[idx - 1])),
))
};
return (, Some());
}
(, None)
}
/// 截取缠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 _))?;
Some([_idx..=_idx].to_vec())
}
}
impl crate::types::fractal:: for K线 {
fn (&self) -> f64 {
self.
}
fn (&self) -> f64 {
self.
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::;
fn make_普K(: i64, : f64, : f64, : f64, : f64, : i64) -> K线 {
K线::K("test", , , , , , 1000.0, , 60)
}
#[test]
fn test_创建缠K_basic() {
let pk = Rc::new(make_普K(1000, 100.0, 110.0, 95.0, 105.0, 0));
let ck = K线::K(1000, 110.0, 95.0, ::, None, 0, pk, None);
assert_eq!(ck., 110.0);
assert_eq!(ck., 95.0);
assert_eq!(ck., 0);
}
#[test]
fn test_分析_empty_sequence() {
let config = ::default();
let mut 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);
assert_eq!(, "新建");
assert_eq!(K序列.len(), 1);
assert!(.is_none()); // 不够3根
}
#[test]
fn test_分析_three_bars_fractal() {
let config = ::default();
let mut K序列 = Vec::new();
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);
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);
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);
assert!(.is_some()); // 分型产生了
}
}
+2
View File
@@ -0,0 +1,2 @@
pub mod bar;
pub mod chan_kline;
+11
View File
@@ -0,0 +1,11 @@
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
pub mod algorithm;
pub mod business;
pub mod config;
pub mod indicators;
pub mod kline;
pub mod structure;
pub mod types;
pub mod utils;
+34
View File
@@ -0,0 +1,34 @@
use chanlun::business::observer::;
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
eprintln!("用法: {} <.nb文件路径>", args[0]);
std::process::exit(1);
}
let = &args[1];
println!("读取文件: {}", );
match ::(, None) {
Ok() => {
println!("符号: {}", .);
println!("周期: {}", .);
println!("普K数量: {}", .K线序列.len());
println!("缠K数量: {}", .K线序列.len());
println!("分型数量: {}", ..len());
println!("笔数量: {}", ..len());
println!("笔中枢数量: {}", ._中枢序列.len());
println!("线段数量: {}", .线.len());
println!("中枢数量: {}", ..len());
println!("\n===== 数据分析 =====\n");
._保存数据(None);
}
Err(e) => {
eprintln!("读取失败: {}", e);
std::process::exit(1);
}
}
}
+771
View File
@@ -0,0 +1,771 @@
use crate::algorithm::hub::;
use crate::config::;
use crate::kline::chan_kline::K线;
use crate::kline::bar::K线;
use crate::structure::fractal_obj::;
use crate::structure::segment_feat::线;
use crate::types::{, , };
use std::rc::Rc;
/// 虚线 — 笔和线段的通用数据结构
///
/// 笔和线段共享此 struct,通过 `标识` 字段区分 ("笔"/"线段"/"扩展线段"等)
#[derive(Debug, Clone)]
pub struct 线 {
pub : String,
pub : i64,
pub : i64,
pub : Rc<>,
pub : Rc<>,
pub : bool,
pub : Vec<Rc<线>>,
pub : Vec<Option<Rc<线>>>,
pub _中枢序列: Vec<Rc<>>,
pub _中枢序列: Vec<Rc<>>,
pub _中枢序列: Vec<Rc<>>,
pub K线: Option<Rc<K线>>,
pub : String,
pub _特征序列_显示: bool,
pub : Option<>,
pub : Option<Rc<线>>,
pub : bool,
}
impl 线 {
pub fn new(: i64, : String, : Rc<>, : Rc<>, : i64, : bool) -> Self {
Self {
,
,
,
,
,
,
: Vec::new(),
: Vec::new(),
_中枢序列: Vec::new(),
_中枢序列: Vec::new(),
_中枢序列: Vec::new(),
K线: None,
: "文武".into(),
_特征序列_显示: false,
: None,
: None,
: false,
}
}
/// 笔序列(基础序列的别名)
pub fn (&self) -> &Vec<Rc<线>> {
&self.
}
pub fn (&self) -> String {
format!("{}:{}:{}:{}", self..., self..., self., self.)
}
/// 方向 — 文到武的方向
pub fn (&self) -> {
match (self.., self..) {
(::, ::) => ::,
(::, ::) => ::,
(::, ::) => ::,
(::, ::) => ::,
_ => ::,
}
}
/// 虚线高 — 文和武中高的那个
pub fn (&self) -> f64 {
self...max(self..)
}
/// 虚线低 — 文和武中低的那个
pub fn (&self) -> f64 {
self...min(self..)
}
/// 判断两个虚线是否首尾相连
pub fn (&self, : &线) -> bool {
self... == ...
}
/// 判断两个虚线是否首尾相连
pub fn (&self, : &线) -> bool {
self... == ...
}
/// 获取该虚线范围内的普K序列
pub fn K序列(&self, K序列: &[Rc<K线>]) -> Vec<Rc<K线>> {
let = self... as usize;
let = self... as usize;
if < K序列.len() && < K序列.len() && <= {
K序列[..=].to_vec()
} else {
Vec::new()
}
}
/// 获取该虚线范围内的缠K序列
pub fn K序列(&self, K序列: &[Rc<K线>]) -> Vec<Rc<K线>> {
K线::(K序列, &self.., &self..).unwrap_or_default()
}
/// 获取数据文本(用于保存/调试)
pub fn (&self) -> String {
use crate::utils::format_f64_g;
if self. == "" {
return format!(
"{}, {}, {}, 文:({},{}), 武:({},{}), {}",
self.,
self.,
self.,
self..,
format_f64_g(self..),
self..,
format_f64_g(self..),
if self. { "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. {
Some(g) => format!("{}", g),
None => "None".to_string(),
};
let _str = match &self. {
Some(d) => format!("{}", d),
None => "None".to_string(),
};
// 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!("[{}]", .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.,
self.,
self.,
self..,
format_f64_g(self..),
self..,
format_f64_g(self..),
if self. { "True" } else { "False" },
self..len(),
_bool(_a),
_bool(_b),
_bool(_c),
_str,
_str,
_str,
match &穿 { Some(d) => format!("{}", d), None => "None".to_string() },
_str,
_str,
_str,
self.,
_str,
_str,
if self. { "True" } else { "False" },
)
}
// ---- 关联函数(静态工厂方法) ----
/// 创建笔
pub fn (: Rc<>, : Rc<>, : bool) -> Self {
Self::new(0, "".into(), , , 1, )
}
/// 创建线段
pub fn 线(线: &[Rc<线>]) -> Self {
let = Rc::clone(&线[0].);
let = Rc::clone(&线[线.len() - 1].);
let = if 线[0]. == "" {
"线段".into()
} else {
format!("线段<{}>", 线[0].)
};
let = 线[0]. + 1;
let mut = Self::new(0, , , , , true);
. = 线.to_vec();
. = "文武".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序列: &[Rc<K线>], 线: &线) -> f64 {
let K线序列 = K线::rc(K序列, &线...K线, &线...K线);
if K线序列.is_empty() {
return 0.0;
}
let : f64 = K线序列
.iter()
.filter_map(|k| k.macd.as_ref())
.map(|m| m.MACD柱.abs())
.sum();
/ K线序列.len() as f64
}
/// 计算MACD柱子均值_阴 — 负柱的绝对值均值
pub fn MACD柱子均值_阴(K序列: &[Rc<K线>], 线: &线) -> Option<f64> {
let K线序列 = K线::rc(K序列, &线...K线, &线...K线);
let : Vec<f64> = K线序列
.iter()
.filter_map(|k| k.macd.as_ref())
.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柱子均值_阳 — 正柱的绝对值均值
pub fn MACD柱子均值_阳(K序列: &[Rc<K线>], 线: &线) -> Option<f64> {
let K线序列 = K线::rc(K序列, &线...K线, &线...K线);
let : Vec<f64> = K线序列
.iter()
.filter_map(|k| k.macd.as_ref())
.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序列: &[Rc<K线>], 线: &线) -> bool {
let _MACD = match &线...K线.macd {
Some(m) => m.MACD柱.abs(),
None => return false,
};
_MACD < Self::MACD柱子均值(K序列, 线)
}
/// 武之MACD均值 — 按方向选择阴/阳均值比对
pub fn MACD均值(K序列: &[Rc<K线>], 线: &线) -> bool {
if 线.() == :: {
Self::MACD均值_阳(K序列, 线)
} else {
Self::MACD均值_阴(K序列, 线)
}
}
/// 武之MACD均值_阴 — 武端负柱是否小于阴均值
pub fn MACD均值_阴(K序列: &[Rc<K线>], 线: &线) -> bool {
let _MACD = match &线...K线.macd {
Some(m) => m.MACD柱.abs(),
None => return false,
};
match Self::MACD柱子均值_阴(K序列, 线) {
Some() => _MACD < .abs(),
None => false,
}
}
/// 武之MACD均值_阳 — 武端正柱是否小于阳均值
pub fn MACD均值_阳(K序列: &[Rc<K线>], 线: &线) -> bool {
let _MACD = match &线...K线.macd {
Some(m) => m.MACD柱.abs(),
None => return false,
};
match Self::MACD柱子均值_阳(K序列, 线) {
Some() => _MACD < ,
None => false,
}
}
/// 武之MACD极值 — 武端MACD柱是否为区间极值
pub fn MACD极值(K序列: &[Rc<K线>], 线: &线) -> bool {
let _MACD = match &线...K线.macd {
Some(m) => m.MACD柱,
None => return false,
};
let K线序列 = K线::rc(K序列, &线...K线, &线...K线);
let : Vec<f64> = K线序列
.iter()
.filter_map(|k| k.macd.as_ref())
.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序列: &[Rc<K线>], : ) -> [bool; 3] {
if K序列.is_empty() {
return [false, false, false];
}
let = &K序列[K序列.len() - 1];
if == :: {
let : Vec<&Rc<K线>> = K序列
.iter()
.filter(|k| k.macd.as_ref().map_or(false, |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.macd.as_ref().unwrap().MACD柱
.partial_cmp(&b.macd.as_ref().unwrap().MACD柱)
.unwrap()
})
.unwrap();
let mut = vec![Rc::clone(*), Rc::clone()];
.sort_by_key(|k| k.);
if let (Some(m0), Some(m1)) = ([0].macd.as_ref(), [1].macd.as_ref()) {
if m0.MACD柱 > m1.MACD柱 && [0]. < [1]. {
[0] = true;
}
}
// DIF背驰
let =
.iter()
.max_by(|a, b| {
let da = a.macd.as_ref().and_then(|m| m.DIF).unwrap_or(0.0);
let db = b.macd.as_ref().and_then(|m| m.DIF).unwrap_or(0.0);
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()) {
let dif0 = m0.DIF.unwrap_or(0.0);
let dif1 = m1.DIF.unwrap_or(0.0);
if dif0 > dif1 && 2[0]. < 2[1]. {
[1] = true;
}
}
// DEA背驰
let 线 =
.iter()
.max_by(|a, b| {
let da = a.macd.as_ref().and_then(|m| m.DEA).unwrap_or(0.0);
let db = b.macd.as_ref().and_then(|m| m.DEA).unwrap_or(0.0);
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()) {
let dea0 = m0.DEA.unwrap_or(0.0);
let dea1 = m1.DEA.unwrap_or(0.0);
if dea0 > dea1 && 3[0]. < 3[1]. {
[2] = true;
}
}
} else {
let : Vec<&Rc<K线>> = K序列
.iter()
.filter(|k| k.macd.as_ref().map_or(false, |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.macd.as_ref().unwrap().MACD柱.abs()
.partial_cmp(&b.macd.as_ref().unwrap().MACD柱.abs())
.unwrap()
})
.unwrap();
let mut = vec![Rc::clone(*), Rc::clone()];
.sort_by_key(|k| k.);
if let (Some(m0), Some(m1)) = ([0].macd.as_ref(), [1].macd.as_ref()) {
if m0.MACD柱 < m1.MACD柱 && [0]. > [1]. {
[0] = true;
}
}
// DIF背驰
let =
.iter()
.max_by(|a, b| {
let da = a.macd.as_ref().and_then(|m| m.DIF).unwrap_or(0.0).abs();
let db = b.macd.as_ref().and_then(|m| m.DIF).unwrap_or(0.0).abs();
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()) {
let dif0 = m0.DIF.unwrap_or(0.0);
let dif1 = m1.DIF.unwrap_or(0.0);
if dif0 < dif1 && 2[0]. > 2[1]. {
[1] = true;
}
}
// DEA背驰
let 线 =
.iter()
.max_by(|a, b| {
let da = a.macd.as_ref().and_then(|m| m.DEA).unwrap_or(0.0).abs();
let db = b.macd.as_ref().and_then(|m| m.DEA).unwrap_or(0.0).abs();
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()) {
let dea0 = m0.DEA.unwrap_or(0.0);
let dea1 = m1.DEA.unwrap_or(0.0);
if dea0 < dea1 && 3[0]. > 3[1]. {
[2] = true;
}
}
}
}
// ---- MACD柱子分段 ----
/// 计算MACD柱子分段 — 按正负号将MACD柱子分段
pub fn MACD柱子分段(k线序列: &[Rc<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].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线.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序列: &[Rc<K线>], : usize, : usize) -> std::collections::HashMap<String, String> {
use std::collections::HashMap;
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 = &K序列[i - 1].macd;
let cur = &K序列[i].macd;
if pre.is_none() || cur.is_none() {
continue;
}
let (pre_dif, cur_dif) = (pre.as_ref().unwrap().DIF, cur.as_ref().unwrap().DIF);
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 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 = &K序列[i - 1].macd;
let cur = &K序列[i].macd;
if pre.is_none() || cur.is_none() {
.push(0);
continue;
}
let pre_dif = pre.as_ref().unwrap().DIF;
let pre_dea = pre.as_ref().unwrap().DEA;
let cur_dif = cur.as_ref().unwrap().DIF;
let cur_dea = cur.as_ref().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::(&, , );
let mut map = HashMap::new();
map.insert("DIF上穿0".into(), dif_up.to_string());
map.insert("DIF下穿0".into(), dif_down.to_string());
map.insert("DEA上穿0".into(), dea_up.to_string());
map.insert("DEA下穿0".into(), dea_down.to_string());
map.insert("金叉次数".into(), golden.to_string());
map.insert("死叉次数".into(), death.to_string());
map.insert("密集交叉区域".into(), format!("{:?}", ));
map
}
// ---- 买卖意义 ----
/// 买卖意义 — 核心买卖点判断逻辑
///
/// 返回 (是否有意义, 原因字符串)
pub fn (线: &线, : &crate::business::observer::) -> (bool, String) {
let K序列 = &.K线序列;
let = &.;
if 线. != "" && 线. != "线段" && !线..starts_with("线段<") {
return (false, "标识不在范围内".into());
}
// KDJ指标完整性检查
match &线...K线.kdj {
Some(kdj) if kdj.K.is_some() && kdj.D.is_some() && kdj.J.is_some() => {}
_ => return (false, "KDJ指标不完整".into()),
}
let = Self::K买卖点模式(&._指标模式, &线.., );
let = false;
let : Vec<Rc<>> = if 线. == "" {
crate::algorithm::bi::::(线, )
} else {
crate::algorithm::segment::线::(线, )
};
if {
if 线. == "" {
if Self::MACD均值(K序列, 线) {
return (true, "武之MACD均值".into());
}
if Self::MACD极值(K序列, 线) && !.is_empty() {
return (true, "背驰过且极值".into());
} else if 线..MACD柱子分型匹配() {
return (
true,
format!(
"背驰过:{},极值:{},柱子分型匹配",
.len(),
Self::MACD极值(K序列, 线)
),
);
}
}
if 线. != ""
&& crate::algorithm::segment::线::线(线, )
{
return (true, "线段内部背驰".into());
}
}
if ! && && 线...MACD柱子匹配() {
if Self::MACD极值(K序列, 线) && .len() > 2 {
return (
true,
"没结果, 极值, 柱子分型匹配, 背驰过大于2次".into(),
);
}
}
(, "".into())
}
}
impl std::fmt::Display for 线 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self. == "" {
write!(
f,
"笔({}, {}, {}, {}, 周期: {}, 数量: {})",
self.,
self.(),
self.,
self.,
self...,
self... - self... + 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线 {
Some(k) => format!("{}", k),
None => "None".to_string(),
};
write!(
f,
"{}<{}, {}, {}, {}, {}, 数量: {}, 缺口: {}, {}>",
self.,
self.,
,
self.(),
self.,
self.,
self..len(),
_str,
K线_str,
)
}
}
}
+24
View File
@@ -0,0 +1,24 @@
use crate::structure::segment_feat::线;
use crate::types::;
use std::rc::Rc;
/// 特征分型 — 由三个线段特征元素构成的分型
#[derive(Debug, Clone)]
pub struct {
pub : Rc<线>,
pub : Rc<线>,
pub : Rc<线>,
pub : ,
}
impl {
pub fn new(: Rc<线>, : Rc<线>, : Rc<线>, : ) -> Self {
Self { , , , }
}
}
impl std::fmt::Display for {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "特征分型<{}, {}>", self., self.)
}
}
+173
View File
@@ -0,0 +1,173 @@
use crate::kline::chan_kline::K线;
use crate::types::;
use crate::types::;
use std::rc::Rc;
/// 分型 — 由三根缠K构成(可能缺左或右)
#[derive(Debug, Clone)]
pub struct {
pub : Option<Rc<K线>>,
pub : Rc<K线>,
pub : Option<Rc<K线>>,
pub : ,
pub : i64,
pub : f64,
}
impl {
pub fn new(: Option<Rc<K线>>, : Rc<K线>, : Option<Rc<K线>>) -> Self {
let = ..unwrap_or(::);
let = .;
let = .;
Self {
,
,
,
,
,
,
}
}
/// 左中右三组关系
pub fn (&self) -> Option<(, , )> {
let = self..as_ref()?;
let = self..as_ref()?;
Some((
::(., ., self.., self..),
::(self.., self.., ., .),
::(., ., ., .),
))
}
/// 分型强度
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,
}
}
/// MACD柱子分型匹配
pub fn MACD柱子分型匹配(&self) -> bool {
match self. {
:: | :: => {
if let Some(ref ) = self. {
.MACD柱子匹配()
} else {
false
}
}
:: | :: => {
if let Some(ref ) = self. {
.MACD柱子匹配()
} else {
false
}
}
:: => false,
}
}
/// 判断两个分型是否匹配
pub fn (: &, : &, : &str) -> bool {
match {
"" => .. == ..,
_ => 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())?;
let = if idx > 0 {
Some(Rc::clone(&K线序列[idx - 1]))
} else {
None
};
let = if idx + 1 < K线序列.len() {
Some(Rc::clone(&K线序列[idx + 1]))
} else {
None
};
Some(Self::new(, Rc::clone(), ))
}
/// 向分型序列中添加新分型
pub fn (: &mut Vec<Rc<>>, : Rc<>) {
if let Some() = .last() {
if . == . {
// 同一时间戳: 比较强度,保留更强的
match . {
:: | :: => {
if . >= . {
.pop();
.push();
}
return;
}
:: | :: => {
if . <= . {
.pop();
.push();
}
return;
}
:: => {}
}
}
// 相同结构只保留更强的
if . == . {
if (. == :: || . == ::)
&& . >= .
{
.pop();
.push();
} else if (. == :: || . == ::)
&& . <= .
{
.pop();
.push();
}
return;
}
}
.push();
}
}
impl crate::types::fractal:: for {
fn (&self) -> f64 {
self..
}
fn (&self) -> f64 {
self..
}
}
impl std::fmt::Display for {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}<{}, {}, None: {}, None: {}>",
self...unwrap_or(crate::types::::),
self.,
crate::utils::format_f64_g(self.),
if self..is_none() { "True" } else { "False" },
if self..is_none() { "True" } else { "False" },
)
}
}
+4
View File
@@ -0,0 +1,4 @@
pub mod dash_line;
pub mod feat_fractal;
pub mod fractal_obj;
pub mod segment_feat;
+254
View File
@@ -0,0 +1,254 @@
use crate::structure::dash_line::线;
use crate::structure::feat_fractal::;
use crate::structure::fractal_obj::;
use crate::types::{, };
use std::rc::Rc;
/// 线段特征 — 特征序列元素(内部是虚线的集合)
#[derive(Debug, Clone)]
pub struct 线 {
pub : i64,
pub : String,
pub 线: ,
pub : Vec<Rc<线>>,
}
impl 线 {
pub fn new(: String, : Vec<Rc<线>>, 线: ) -> Self {
Self {
: 0,
,
线,
: ,
}
}
pub fn (&self) -> String {
self..clone()
}
/// 文 — 取特征序列元素中分型特征值最大/最小的文分型
pub fn (&self) -> Rc<> {
if self.线.() {
// 向上 → 取最大的文
self.
.iter()
.max_by(|a, b| {
a.
.
.partial_cmp(&b..)
.unwrap_or(std::cmp::Ordering::Equal)
})
.map(|x| Rc::clone(&x.))
.unwrap_or_else(|| Rc::clone(&self.[0].))
} else {
// 向下 → 取最小的文
self.
.iter()
.min_by(|a, b| {
a.
.
.partial_cmp(&b..)
.unwrap_or(std::cmp::Ordering::Equal)
})
.map(|x| Rc::clone(&x.))
.unwrap_or_else(|| Rc::clone(&self.[0].))
}
}
/// 武 — 取特征序列元素中分型特征值最大/最小的武分型
pub fn (&self) -> Rc<> {
if self.线.() {
self.
.iter()
.max_by(|a, b| {
a.
.
.partial_cmp(&b..)
.unwrap_or(std::cmp::Ordering::Equal)
})
.map(|x| Rc::clone(&x.))
.unwrap_or_else(|| Rc::clone(&self.[0].))
} else {
self.
.iter()
.min_by(|a, b| {
a.
.
.partial_cmp(&b..)
.unwrap_or(std::cmp::Ordering::Equal)
})
.map(|x| Rc::clone(&x.))
.unwrap_or_else(|| Rc::clone(&self.[0].))
}
}
pub fn (&self) -> f64 {
let = self.();
let = self.();
..max(.)
}
pub fn (&self) -> f64 {
let = self.();
let = self.();
..min(.)
}
/// 方向 — 线段方向的翻转
pub fn (&self) -> {
self.线.()
}
/// 向特征序列元素中添加虚线
pub fn (&mut self, 线: Rc<线>) -> Result<(), String> {
if 线.() == self.线 {
return Err("添加方向与线段方向相同".into());
}
self..push(线);
Ok(())
}
/// 新建特征序列元素
pub fn (线: Vec<Rc<线>>, 线: ) -> Self {
let = format!("特征<虚线>");
Self::new(, 线, 线)
}
/// 静态分析 — 从虚线序列生成特征序列元素列表
pub fn (
线: &[Rc<线>],
线: ,
: &str,
: bool,
) -> Vec<Rc<线>> {
let mut : Vec<Rc<线>> = Vec::new();
// 需要被合并的方向集合
let : Vec<> = match {
"老阳" | "老阴" if ! => vec![::, ::, ::],
_ => vec![::, ::],
};
for 线 in 线 {
// 情况1:方向相同(可能触发分型替换)
if 线.() == 线 {
if .len() >= 3 {
let = Rc::clone(&[.len() - 3]);
let = Rc::clone(&[.len() - 2]);
let = Rc::clone(&[.len() - 1]);
if let Some() = ::(&*, &*, &*, true, true) {
let = (线 == ::
&& == ::
&& 线.() > .())
|| (线 == ::
&& == ::
&& 线.() < .());
if {
let 线 = if .[0]. < .[0]. {
&.[0]
} else {
&.[0]
};
let 线 = if ..last().unwrap().
> ..last().unwrap().
{
&.[..len() - 1]
} else {
&.[..len() - 1]
};
let fake = 线::(
Rc::clone(&线.),
Rc::clone(&线.),
false,
);
.pop();
let idx = .len() - 1;
[idx] =
Rc::new(Self::(vec![Rc::new(fake)], 线));
}
}
}
continue;
}
// 情况2:方向不同(执行特征序列的合并/添加)
if .is_empty() {
.push(Rc::new(Self::(vec![Rc::clone(线)], 线)));
continue;
}
// 检查与最后一个特征序列的方向关系
let _idx = .len() - 1;
let 线 = &*[_idx];
if .contains(&::(
线.(),
线.(),
线.(),
线.(),
)) {
// Clone-modify-replace
let mut = (*[_idx]).clone();
let _ = .(Rc::clone(线));
[_idx] = Rc::new();
} else {
.push(Rc::new(Self::(vec![Rc::clone(线)], 线)));
}
}
}
/// 获取分型序列
pub fn (: &[Rc<线>]) -> Vec<> {
let mut = Vec::new();
if .len() < 3 {
return ;
}
for i in 2...len() {
let = Rc::clone(&[i - 2]);
let = Rc::clone(&[i - 1]);
let = Rc::clone(&[i]);
if let Some() = ::_对象(
&* as &dyn crate::types::fractal::,
&* as &dyn crate::types::fractal::,
&* as &dyn crate::types::fractal::,
true,
true,
) {
.push(::new(, , , ));
}
}
}
}
impl crate::types::fractal:: for 线 {
fn (&self) -> f64 {
self.()
}
fn (&self) -> f64 {
self.()
}
}
impl std::fmt::Display for 线 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self..is_empty() {
write!(f, "{}<{}, 空>", self., self.线)
} else {
write!(
f,
"{}<{}, {}, {}, {}>",
self.,
self.线,
self.(),
self.(),
self..len()
)
}
}
}
+99
View File
@@ -0,0 +1,99 @@
use serde::{Deserialize, Serialize};
/// 买卖点类型 —— 六类买卖点(一二三 + T1/T1P/T2/T2S/T3A/T3B
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum {
#[serde(rename = "一买")]
,
#[serde(rename = "一卖")]
,
#[serde(rename = "二买")]
,
#[serde(rename = "二卖")]
,
#[serde(rename = "三买")]
,
#[serde(rename = "三卖")]
,
#[serde(rename = "T1买")]
T1买,
#[serde(rename = "T1卖")]
T1卖,
#[serde(rename = "T1P买")]
T1P买,
#[serde(rename = "T1P卖")]
T1P卖,
#[serde(rename = "T2买")]
T2买,
#[serde(rename = "T2卖")]
T2卖,
#[serde(rename = "T2S买")]
T2S买,
#[serde(rename = "T2S卖")]
T2S卖,
#[serde(rename = "T3A买")]
T3A买,
#[serde(rename = "T3A卖")]
T3A卖,
#[serde(rename = "T3B买")]
T3B买,
#[serde(rename = "T3B卖")]
T3B卖,
}
impl std::fmt::Display for {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self:: => write!(f, "一买"),
Self:: => write!(f, "一卖"),
Self:: => write!(f, "二买"),
Self:: => write!(f, "二卖"),
Self:: => write!(f, "三买"),
Self:: => write!(f, "三卖"),
Self::T1买 => write!(f, "T1买"),
Self::T1卖 => write!(f, "T1卖"),
Self::T1P买 => write!(f, "T1P买"),
Self::T1P卖 => write!(f, "T1P卖"),
Self::T2买 => write!(f, "T2买"),
Self::T2卖 => write!(f, "T2卖"),
Self::T2S买 => write!(f, "T2S买"),
Self::T2S卖 => write!(f, "T2S卖"),
Self::T3A买 => write!(f, "T3A买"),
Self::T3A卖 => write!(f, "T3A卖"),
Self::T3B买 => write!(f, "T3B买"),
Self::T3B卖 => write!(f, "T3B卖"),
}
}
}
impl {
pub fn (&self) -> bool {
matches!(
self,
Self::
| Self::
| Self::
| Self::T1买
| Self::T1P买
| Self::T2买
| Self::T2S买
| Self::T3A买
| Self::T3B买
)
}
pub fn (&self) -> bool {
matches!(
self,
Self::
| Self::
| Self::
| Self::T1卖
| Self::T1P卖
| Self::T2卖
| Self::T2S卖
| Self::T3A卖
| Self::T3B卖
)
}
}
+108
View File
@@ -0,0 +1,108 @@
use serde::{Deserialize, Serialize};
/// 相对方向 —— K线之间的相对位置关系
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum {
#[serde(rename = "交叠向上")]
,
#[serde(rename = "交叠向下")]
,
#[serde(rename = "向上缺口")]
,
#[serde(rename = "向下缺口")]
,
#[serde(rename = "衔接向上")]
,
#[serde(rename = "衔接向下")]
,
#[serde(rename = "顺序包含")]
,
#[serde(rename = "逆序包含")]
,
#[serde(rename = "相同包含")]
,
}
impl {
pub fn (&self) -> Self {
match self {
Self:: => Self::,
Self:: => Self::,
Self:: => Self::,
Self:: => Self::,
Self:: => Self::,
Self:: => Self::,
Self:: => Self::,
Self:: => Self::,
other => *other,
}
}
pub fn (&self) -> bool {
matches!(self, Self:: | Self:: | Self::)
}
pub fn (&self) -> bool {
matches!(self, Self:: | Self:: | Self::)
}
pub fn (&self) -> bool {
matches!(self, Self:: | Self:: | Self::)
}
pub fn (&self) -> bool {
matches!(self, Self:: | Self::)
}
pub fn (&self) -> bool {
matches!(self, Self:: | Self::)
}
/// 分析两个K线之间的相对方向
pub fn (: f64, : f64, : f64, : f64) -> Self {
if ( - ).abs() < f64::EPSILON && ( - ).abs() < f64::EPSILON {
return Self::;
}
if > && > {
if ( - ).abs() < f64::EPSILON {
return Self::;
}
if > {
return Self::;
}
return Self::;
}
if < && < {
if ( - ).abs() < f64::EPSILON {
return Self::;
}
if < {
return Self::;
}
return Self::;
}
if >= && <= {
return Self::;
}
if <= && >= {
return Self::;
}
panic!("无法识别的方向: 前({},{}), 后({},{})", , , , );
}
}
impl std::fmt::Display for {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self:: => write!(f, "相对方向.向上"),
Self:: => write!(f, "相对方向.向下"),
Self:: => write!(f, "相对方向.向上缺口"),
Self:: => write!(f, "相对方向.向下缺口"),
Self:: => write!(f, "相对方向.衔接向上"),
Self:: => write!(f, "相对方向.衔接向下"),
Self:: => write!(f, "相对方向.顺"),
Self:: => write!(f, "相对方向.逆"),
Self:: => write!(f, "相对方向.同"),
}
}
}
+109
View File
@@ -0,0 +1,109 @@
use serde::{Deserialize, Serialize};
/// 分型结构 —— 三根K线构成的结构形态
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum {
#[serde(rename = "三连向上")]
,
#[serde(rename = "三连向下")]
,
#[serde(rename = "顶分型")]
,
#[serde(rename = "底分型")]
,
#[serde(rename = "向右扩散")]
,
}
impl std::fmt::Display for {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self:: => write!(f, ""),
Self:: => write!(f, ""),
Self:: => write!(f, ""),
Self:: => write!(f, ""),
Self:: => write!(f, ""),
}
}
}
impl {
/// 分析三根K线构成的分型结构(泛型版本)
pub fn (
: &impl ,
: &impl ,
: &impl ,
: bool,
: bool,
) -> Option<Self> {
Self::_内部(.(), .(), .(), .(), .(), .(), , )
}
/// 分析三元素构成的分型结构(trait object 版本)
pub fn _对象(
: &dyn ,
: &dyn ,
: &dyn ,
: bool,
: bool,
) -> Option<Self> {
Self::_内部(.(), .(), .(), .(), .(), .(), , )
}
fn _内部(
: f64, : f64,
: f64, : f64,
: f64, : f64,
: bool,
: bool,
) -> Option<Self> {
use crate::types::direction::;
let = ::(, , , );
let = ::(, , , );
let = |d: | d.();
let = |d: | d.();
match (, ) {
// 顺序包含 — 忽视时可以绕过
(::, _) if ! => {
panic!("顺序包含: {:?} {:?}", , );
}
(_, ::) if ! => {
panic!("顺序包含: {:?} {:?}", , );
}
// 向上 + 向上 = 三连上
(a, b) if (a) && (b) => Some(Self::),
// 向上 + 向下 = 顶分型
(a, b) if (a) && (b) => Some(Self::),
// 向上 + 逆序包含 = 上
(a, ::) if (a) && => Some(Self::),
// 向下 + 向上 = 底分型
(a, b) if (a) && (b) => Some(Self::),
// 向下 + 向下 = 三连下
(a, b) if (a) && (b) => Some(Self::),
// 向下 + 逆序包含 = 下
(a, ::) if (a) && => Some(Self::),
// 逆序包含 + 向上 = 底
(::, a) if (a) && => Some(Self::),
// 逆序包含 + 向下 = 顶
(::, a) if (a) && => Some(Self::),
// 逆序包含 + 逆序包含 = 散
(::, ::) if => Some(Self::),
_ => {
eprintln!(
"无法识别的分型结构: 左中={:?}, 中右={:?}",
,
);
None
}
}
}
}
/// Trait for types that have 高 and 低 (used by 分型结构::分析)
pub trait {
fn (&self) -> f64;
fn (&self) -> f64;
}
+51
View File
@@ -0,0 +1,51 @@
use serde::{Deserialize, Serialize};
/// 缺口 —— 两个价格区间之间的空隙
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct {
pub : f64,
pub : f64,
}
impl {
pub fn new(: f64, : f64) -> Self {
assert!( > , "缺口高必须大于低: 高={高}, 低={低}");
Self { , }
}
/// 在 [起点, 终点] 区间内居中截取一个子区间
pub fn (: f64, : f64, : f64) -> Option<Self> {
let (, ) = if <= {
(, )
} else {
(, )
};
let = - ;
let = * ;
let = ( + ) / 2.0;
let = - ;
let = + ;
if > || < {
return None;
}
let = .max();
let = .min();
Some(Self::new(.max(), .min()))
}
}
impl std::fmt::Display for {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"缺口区间<{} <=> {}>",
crate::utils::format_f64_g(self.),
crate::utils::format_f64_g(self.)
)
}
}
+9
View File
@@ -0,0 +1,9 @@
pub mod bsp_type;
pub mod direction;
pub mod fractal;
pub mod gap;
pub use bsp_type::;
pub use direction::;
pub use fractal::;
pub use gap::;
+27
View File
@@ -0,0 +1,27 @@
use chrono::DateTime;
/// 将多种类型统一转为时间戳 (Unix epoch 秒)
pub fn (ts: &str) -> Option<i64> {
// 尝试解析为整数时间戳
if let Ok(v) = ts.parse::<i64>() {
return Some(v);
}
// 尝试解析为浮点时间戳
if let Ok(v) = ts.parse::<f64>() {
return Some(v as i64);
}
// 尝试 ISO 格式日期字符串
if let Ok(dt) = DateTime::parse_from_rfc3339(ts) {
return Some(dt.timestamp());
}
// 尝试 "YYYY-MM-DD HH:MM:SS" 格式
if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(ts, "%Y-%m-%d %H:%M:%S") {
return Some(dt.and_utc().timestamp());
}
None
}
/// 将多种类型转为时间戳数字 (i64)
pub fn _数字(ts: &str) -> Option<i64> {
(ts)
}
+14
View File
@@ -0,0 +1,14 @@
/// Format f64 with Python :g semantics — strip trailing zeros, no scientific notation for common values
pub fn format_f64_g(value: f64) -> String {
if value.is_nan() {
return "nan".to_string();
}
if value.is_infinite() {
return if value > 0.0 { "inf".to_string() } else { "-inf".to_string() };
}
// Use high precision then trim trailing zeros
let s = format!("{:.15}", value);
let s = s.trim_end_matches('0');
s.trim_end_matches('.').to_string()
}
+5
View File
@@ -0,0 +1,5 @@
pub mod datetime;
pub mod format;
pub use datetime::{, _数字};
pub use format::format_f64_g;