第一版

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
+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;