第八版
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "chanlun-py"
|
||||
version = "26.5.67"
|
||||
version = "26.5.86"
|
||||
edition = "2021"
|
||||
description = "缠论技术分析库 — Rust 高性能 Python 绑定"
|
||||
authors = ["YuYuKunKun"]
|
||||
@@ -12,7 +12,7 @@ crate-type = ["cdylib"]
|
||||
name = "chanlun"
|
||||
|
||||
[dependencies]
|
||||
chanlun = "26.5.2" # { path = "../chanlun" }
|
||||
chanlun = "26.5.3" # { path = "../chanlun" }
|
||||
pyo3 = { version = "0.28", features = ["extension-module", "experimental-inspect"] }
|
||||
serde_json = "1"
|
||||
chrono = "0.4"
|
||||
|
||||
@@ -16,7 +16,6 @@ __all__ = [
|
||||
"基础买卖点",
|
||||
"平滑异同移动平均线",
|
||||
"指标",
|
||||
"测试_读取数据",
|
||||
"特征分型",
|
||||
"相对强弱指数",
|
||||
"相对方向",
|
||||
|
||||
@@ -11,7 +11,6 @@ __all__ = [
|
||||
"基础买卖点",
|
||||
"平滑异同移动平均线",
|
||||
"指标",
|
||||
"测试_读取数据",
|
||||
"特征分型",
|
||||
"相对强弱指数",
|
||||
"相对方向",
|
||||
|
||||
@@ -16,7 +16,6 @@ __all__ = [
|
||||
"基础买卖点",
|
||||
"平滑异同移动平均线",
|
||||
"指标",
|
||||
"测试_读取数据",
|
||||
"特征分型",
|
||||
"相对强弱指数",
|
||||
"相对方向",
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "chanlun"
|
||||
version = "2605.67"
|
||||
version = "2605.86"
|
||||
description = "缠论技术分析库 — Rust 高性能实现"
|
||||
readme = { file = "README.md", content-type = "text/markdown" }
|
||||
license = { file = "LICENSE", content-type = "text/plain" }
|
||||
|
||||
+177
-148
@@ -22,9 +22,37 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
use crate::kline_py::chan_kline_to_py;
|
||||
use crate::structure_py::{dashed_to_py, fractal_to_py};
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyDict, PyType};
|
||||
use std::rc::Rc;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
thread_local! {
|
||||
static HUB_IDENTITY: RwLock<HashMap<usize, Py<中枢Py>>> = RwLock::new(HashMap::new());
|
||||
}
|
||||
|
||||
pub(crate) fn hub_to_py(
|
||||
py: Python<'_>, inner: Arc<chanlun::algorithm::hub::中枢>
|
||||
) -> Py<中枢Py> {
|
||||
let key = Arc::as_ptr(&inner) as usize;
|
||||
if let Some(cached) =
|
||||
HUB_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
HUB_IDENTITY.with(|c| {
|
||||
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
|
||||
});
|
||||
let obj = Py::new(py, 中枢Py { inner }).unwrap();
|
||||
HUB_IDENTITY.with(|c| {
|
||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
||||
});
|
||||
obj
|
||||
}
|
||||
|
||||
use crate::business_py::观察者Py;
|
||||
use crate::config_py::缠论配置Py;
|
||||
@@ -60,7 +88,7 @@ impl 背驰分析Py {
|
||||
方式: &str,
|
||||
py: Python<'_>,
|
||||
) -> bool {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = K线序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = K线序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -107,7 +135,7 @@ impl 背驰分析Py {
|
||||
普K序列: Vec<Py<K线Py>>,
|
||||
py: Python<'_>,
|
||||
) -> bool {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -127,7 +155,7 @@ impl 背驰分析Py {
|
||||
普K序列: Vec<Py<K线Py>>,
|
||||
py: Python<'_>,
|
||||
) -> bool {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -148,7 +176,7 @@ impl 背驰分析Py {
|
||||
配置: &Bound<'_, 缠论配置Py>,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<bool> {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -170,7 +198,7 @@ impl 背驰分析Py {
|
||||
普K序列: Vec<Py<K线Py>>,
|
||||
py: Python<'_>,
|
||||
) -> bool {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -192,7 +220,7 @@ impl 背驰分析Py {
|
||||
模式: &str,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<bool> {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -234,13 +262,13 @@ impl 笔Py {
|
||||
配置: &Bound<'_, 缠论配置Py>,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<usize> {
|
||||
let ck_list: Vec<Rc<chanlun::kline::chan_kline::缠论K线>> = 缠K序列
|
||||
let ck_list: Vec<Arc<chanlun::kline::chan_kline::缠论K线>> = 缠K序列
|
||||
.iter()
|
||||
.map(|k| Rc::clone(&k.bind(py).borrow().inner))
|
||||
.map(|k| Arc::clone(&k.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let bi_list: Vec<Rc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
let bi_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let config = 配置.borrow().to_rust_config(py)?;
|
||||
Ok(chanlun::algorithm::bi::笔::获取缠K数量(
|
||||
@@ -255,13 +283,13 @@ impl 笔Py {
|
||||
缠K序列: Vec<Py<crate::kline_py::缠论K线Py>>,
|
||||
取舍: bool,
|
||||
py: Python<'_>,
|
||||
) -> Option<crate::kline_py::缠论K线Py> {
|
||||
let ck_list: Vec<Rc<chanlun::kline::chan_kline::缠论K线>> = 缠K序列
|
||||
) -> Option<Py<crate::kline_py::缠论K线Py>> {
|
||||
let ck_list: Vec<Arc<chanlun::kline::chan_kline::缠论K线>> = 缠K序列
|
||||
.iter()
|
||||
.map(|k| Rc::clone(&k.bind(py).borrow().inner))
|
||||
.map(|k| Arc::clone(&k.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::bi::笔::次高(&ck_list, 取舍)
|
||||
.map(|inner| crate::kline_py::缠论K线Py::from_rc(inner))
|
||||
.map(|inner| crate::kline_py::chan_kline_to_py(py, inner))
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
@@ -271,13 +299,13 @@ impl 笔Py {
|
||||
缠K序列: Vec<Py<crate::kline_py::缠论K线Py>>,
|
||||
取舍: bool,
|
||||
py: Python<'_>,
|
||||
) -> Option<crate::kline_py::缠论K线Py> {
|
||||
let ck_list: Vec<Rc<chanlun::kline::chan_kline::缠论K线>> = 缠K序列
|
||||
) -> Option<Py<crate::kline_py::缠论K线Py>> {
|
||||
let ck_list: Vec<Arc<chanlun::kline::chan_kline::缠论K线>> = 缠K序列
|
||||
.iter()
|
||||
.map(|k| Rc::clone(&k.bind(py).borrow().inner))
|
||||
.map(|k| Arc::clone(&k.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::bi::笔::次低(&ck_list, 取舍)
|
||||
.map(|inner| crate::kline_py::缠论K线Py::from_rc(inner))
|
||||
.map(|inner| crate::kline_py::chan_kline_to_py(py, inner))
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
@@ -287,13 +315,13 @@ impl 笔Py {
|
||||
缠K序列: Vec<Py<crate::kline_py::缠论K线Py>>,
|
||||
取舍: bool,
|
||||
py: Python<'_>,
|
||||
) -> Option<crate::kline_py::缠论K线Py> {
|
||||
let ck_list: Vec<Rc<chanlun::kline::chan_kline::缠论K线>> = 缠K序列
|
||||
) -> Option<Py<crate::kline_py::缠论K线Py>> {
|
||||
let ck_list: Vec<Arc<chanlun::kline::chan_kline::缠论K线>> = 缠K序列
|
||||
.iter()
|
||||
.map(|k| Rc::clone(&k.bind(py).borrow().inner))
|
||||
.map(|k| Arc::clone(&k.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::bi::笔::实际高点(&ck_list, 取舍)
|
||||
.map(|inner| crate::kline_py::缠论K线Py::from_rc(inner))
|
||||
.map(|inner| crate::kline_py::chan_kline_to_py(py, inner))
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
@@ -303,13 +331,13 @@ impl 笔Py {
|
||||
缠K序列: Vec<Py<crate::kline_py::缠论K线Py>>,
|
||||
取舍: bool,
|
||||
py: Python<'_>,
|
||||
) -> Option<crate::kline_py::缠论K线Py> {
|
||||
let ck_list: Vec<Rc<chanlun::kline::chan_kline::缠论K线>> = 缠K序列
|
||||
) -> Option<Py<crate::kline_py::缠论K线Py>> {
|
||||
let ck_list: Vec<Arc<chanlun::kline::chan_kline::缠论K线>> = 缠K序列
|
||||
.iter()
|
||||
.map(|k| Rc::clone(&k.bind(py).borrow().inner))
|
||||
.map(|k| Arc::clone(&k.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::bi::笔::实际低点(&ck_list, 取舍)
|
||||
.map(|inner| crate::kline_py::缠论K线Py::from_rc(inner))
|
||||
.map(|inner| crate::kline_py::chan_kline_to_py(py, inner))
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
@@ -335,9 +363,9 @@ impl 笔Py {
|
||||
文: &Bound<'_, 分型Py>,
|
||||
py: Python<'_>,
|
||||
) -> Option<虚线Py> {
|
||||
let bi_list: Vec<Rc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
let bi_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::bi::笔::以文会友(&bi_list, &文.borrow().inner)
|
||||
.map(|inner| 虚线Py { inner })
|
||||
@@ -351,9 +379,9 @@ impl 笔Py {
|
||||
武: &Bound<'_, 分型Py>,
|
||||
py: Python<'_>,
|
||||
) -> Option<虚线Py> {
|
||||
let bi_list: Vec<Rc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
let bi_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::bi::笔::以武会友(&bi_list, &武.borrow().inner)
|
||||
.map(|inner| 虚线Py { inner })
|
||||
@@ -369,9 +397,9 @@ impl 笔Py {
|
||||
偏移: i64,
|
||||
py: Python<'_>,
|
||||
) -> Option<虚线Py> {
|
||||
let bi_list: Vec<Rc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
let bi_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::bi::笔::根据缠K找笔(&bi_list, &缠K.borrow().inner, 偏移)
|
||||
.map(|inner| 虚线Py { inner })
|
||||
@@ -392,20 +420,20 @@ impl 笔Py {
|
||||
py: Python<'_>,
|
||||
) -> PyResult<i64> {
|
||||
let _ = 递归层次; // Python API 兼容参数,核心从0开始计数
|
||||
let 当前分型_rc = 当前分型.map(|f| Rc::clone(&f.borrow().inner));
|
||||
let mut fr_seq: Vec<Rc<chanlun::structure::fractal_obj::分型>> = 分型序列
|
||||
let 当前分型_rc = 当前分型.map(|f| Arc::clone(&f.borrow().inner));
|
||||
let mut fr_seq: Vec<Arc<chanlun::structure::fractal_obj::分型>> = 分型序列
|
||||
.iter()
|
||||
.map(|f| Rc::clone(&f.bind(py).borrow().inner))
|
||||
.map(|f| Arc::clone(&f.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let mut bi_seq: Vec<Rc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
let mut bi_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let ck_list: Vec<Rc<chanlun::kline::chan_kline::缠论K线>> = 缠K序列
|
||||
let ck_list: Vec<Arc<chanlun::kline::chan_kline::缠论K线>> = 缠K序列
|
||||
.iter()
|
||||
.map(|k| Rc::clone(&k.bind(py).borrow().inner))
|
||||
.map(|k| Arc::clone(&k.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let bar_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let bar_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -435,25 +463,25 @@ impl 笔Py {
|
||||
配置: &Bound<'_, 缠论配置Py>,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<i64> {
|
||||
let mut fr_seq: Vec<Rc<chanlun::structure::fractal_obj::分型>> = 分型序列
|
||||
let mut fr_seq: Vec<Arc<chanlun::structure::fractal_obj::分型>> = 分型序列
|
||||
.iter()
|
||||
.map(|f| Rc::clone(&f.bind(py).borrow().inner))
|
||||
.map(|f| Arc::clone(&f.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let mut bi_seq: Vec<Rc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
let mut bi_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let ck_list: Vec<Rc<chanlun::kline::chan_kline::缠论K线>> = 缠K序列
|
||||
let ck_list: Vec<Arc<chanlun::kline::chan_kline::缠论K线>> = 缠K序列
|
||||
.iter()
|
||||
.map(|k| Rc::clone(&k.bind(py).borrow().inner))
|
||||
.map(|k| Arc::clone(&k.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let bar_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let bar_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
let config = 配置.borrow().to_rust_config(py)?;
|
||||
Ok(chanlun::algorithm::bi::笔::分析递归(
|
||||
Rc::clone(&当前分型.borrow().inner),
|
||||
Arc::clone(&当前分型.borrow().inner),
|
||||
&mut fr_seq,
|
||||
&mut bi_seq,
|
||||
&ck_list,
|
||||
@@ -486,7 +514,7 @@ impl 笔Py {
|
||||
let obs_ref = obs.obs();
|
||||
chanlun::algorithm::bi::笔::获取所有停顿位置(&筆.borrow().inner, &*obs_ref)
|
||||
.into_iter()
|
||||
.map(|d| 虚线Py { inner: Rc::new(d) })
|
||||
.map(|d| 虚线Py { inner: Arc::new(d) })
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -496,12 +524,13 @@ impl 笔Py {
|
||||
_cls: &Bound<'_, PyType>,
|
||||
当前筆: &Bound<'_, 虚线Py>,
|
||||
观察员: &Bound<'_, 观察者Py>,
|
||||
) -> Vec<缠论K线Py> {
|
||||
py: Python<'_>,
|
||||
) -> Vec<Py<缠论K线Py>> {
|
||||
let obs = 观察员.borrow();
|
||||
let obs_ref = obs.obs();
|
||||
chanlun::algorithm::bi::笔::是否背驰过(&当前筆.borrow().inner, &*obs_ref)
|
||||
.into_iter()
|
||||
.map(|ck| 缠论K线Py::from_rc(ck))
|
||||
.map(|ck| chan_kline_to_py(py, ck))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
@@ -538,7 +567,7 @@ impl 线段Py {
|
||||
段: &Bound<'_, 虚线Py>,
|
||||
筆: &Bound<'_, 虚线Py>,
|
||||
) -> PyResult<()> {
|
||||
let bi_rc = Rc::clone(&筆.borrow().inner);
|
||||
let bi_rc = Arc::clone(&筆.borrow().inner);
|
||||
let mut ref_mut = 段.borrow_mut();
|
||||
chanlun::algorithm::segment::线段::添加虚线(&mut ref_mut.inner, bi_rc);
|
||||
Ok(())
|
||||
@@ -555,7 +584,7 @@ impl 线段Py {
|
||||
let mut ref_mut = 段.borrow_mut();
|
||||
chanlun::algorithm::segment::线段::武斗(
|
||||
&mut ref_mut.inner,
|
||||
&Rc::clone(&武.borrow().inner),
|
||||
&Arc::clone(&武.borrow().inner),
|
||||
行号,
|
||||
);
|
||||
Ok(())
|
||||
@@ -578,9 +607,9 @@ impl 线段Py {
|
||||
py: Python<'_>,
|
||||
) -> PyResult<()> {
|
||||
let mut ref_mut = 段.borrow_mut();
|
||||
let rc_list: Vec<Rc<chanlun::structure::dash_line::虚线>> = 序列
|
||||
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::segment::线段::验证序列(&mut ref_mut.inner, &rc_list);
|
||||
Ok(())
|
||||
@@ -595,9 +624,9 @@ impl 线段Py {
|
||||
py: Python<'_>,
|
||||
) -> PyResult<()> {
|
||||
let mut ref_mut = 段.borrow_mut();
|
||||
let rc_list: Vec<Rc<chanlun::structure::dash_line::虚线>> = 序列
|
||||
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::segment::线段::序列重置(&mut ref_mut.inner, &rc_list);
|
||||
Ok(())
|
||||
@@ -659,7 +688,8 @@ impl 线段Py {
|
||||
序列: &Bound<'_, PyAny>,
|
||||
行号: u32,
|
||||
) -> PyResult<()> {
|
||||
let seq: Vec<Option<Rc<chanlun::structure::segment_feat::线段特征>>> = if 序列.is_none() {
|
||||
let seq: Vec<Option<Arc<chanlun::structure::segment_feat::线段特征>>> = if 序列.is_none()
|
||||
{
|
||||
vec![]
|
||||
} else if let Ok(list) = 序列.downcast::<pyo3::types::PyList>() {
|
||||
let mut result = Vec::with_capacity(list.len());
|
||||
@@ -668,7 +698,7 @@ impl 线段Py {
|
||||
result.push(None);
|
||||
} else {
|
||||
let feat: PyRef<'_, 线段特征Py> = item.extract()?;
|
||||
result.push(Some(Rc::clone(&feat.inner)));
|
||||
result.push(Some(Arc::clone(&feat.inner)));
|
||||
}
|
||||
}
|
||||
result
|
||||
@@ -704,6 +734,7 @@ impl 线段Py {
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
#[pyo3(signature = (段, 所属中枢 = None))]
|
||||
/// 将线段基础序列分割为前/后/第三买卖/贯穿伤四部分
|
||||
fn 分割序列(
|
||||
_cls: &Bound<'_, PyType>,
|
||||
@@ -719,7 +750,7 @@ impl 线段Py {
|
||||
let borrowed = 段.borrow();
|
||||
let (a, b, c, d) = if let Some(hub_bound) = 所属中枢 {
|
||||
if let Ok(mut hub_ref) = hub_bound.extract::<PyRefMut<'_, 中枢Py>>() {
|
||||
let inner_mut = Rc::make_mut(&mut hub_ref.inner);
|
||||
let inner_mut = Arc::make_mut(&mut hub_ref.inner);
|
||||
chanlun::algorithm::segment::线段::分割序列(&borrowed.inner, Some(inner_mut))
|
||||
} else {
|
||||
chanlun::algorithm::segment::线段::分割序列(&borrowed.inner, None)
|
||||
@@ -727,7 +758,7 @@ impl 线段Py {
|
||||
} else {
|
||||
chanlun::algorithm::segment::线段::分割序列(&borrowed.inner, None)
|
||||
};
|
||||
let wrap = |v: Vec<Rc<chanlun::structure::dash_line::虚线>>| -> PyResult<Vec<Py<虚线Py>>> {
|
||||
let wrap = |v: Vec<Arc<chanlun::structure::dash_line::虚线>>| -> PyResult<Vec<Py<虚线Py>>> {
|
||||
let mut result = Vec::new();
|
||||
for x in v {
|
||||
result.push(Py::new(py, 虚线Py { inner: x })?);
|
||||
@@ -770,7 +801,7 @@ impl 线段Py {
|
||||
&mut ref_mut.inner,
|
||||
&config,
|
||||
);
|
||||
let pk_list = |v: Vec<Rc<chanlun::algorithm::hub::中枢>>| -> PyResult<Py<PyAny>> {
|
||||
let pk_list = |v: Vec<Arc<chanlun::algorithm::hub::中枢>>| -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for h in v {
|
||||
list.append(Py::new(py, 中枢Py { inner: h })?)?;
|
||||
@@ -791,10 +822,10 @@ impl 线段Py {
|
||||
py: Python<'_>,
|
||||
) -> PyResult<()> {
|
||||
let config = 配置.borrow().to_rust_config(py)?;
|
||||
let mut seg_seq: Vec<Rc<chanlun::structure::dash_line::虚线>> = vec![];
|
||||
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = vec![];
|
||||
chanlun::algorithm::segment::线段::_添加线段(
|
||||
&mut seg_seq,
|
||||
Rc::clone(&待添加线段.borrow().inner),
|
||||
Arc::clone(&待添加线段.borrow().inner),
|
||||
&config,
|
||||
行号,
|
||||
);
|
||||
@@ -812,10 +843,10 @@ impl 线段Py {
|
||||
py: Python<'_>,
|
||||
) -> PyResult<Option<虚线Py>> {
|
||||
let config = 配置.borrow().to_rust_config(py)?;
|
||||
let mut seg_seq: Vec<Rc<chanlun::structure::dash_line::虚线>> = vec![];
|
||||
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = vec![];
|
||||
let result = chanlun::algorithm::segment::线段::_弹出线段(
|
||||
&mut seg_seq,
|
||||
&Rc::clone(&待弹出线段.borrow().inner),
|
||||
&Arc::clone(&待弹出线段.borrow().inner),
|
||||
&config,
|
||||
行号,
|
||||
);
|
||||
@@ -831,9 +862,9 @@ impl 线段Py {
|
||||
层级: i64,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<bool> {
|
||||
let mut seg_seq: Vec<Rc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let config = 配置.borrow().to_rust_config(py)?;
|
||||
Ok(chanlun::algorithm::segment::线段::_缺口突破(
|
||||
@@ -852,9 +883,9 @@ impl 线段Py {
|
||||
层级: i64,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<bool> {
|
||||
let mut seg_seq: Vec<Rc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let config = 配置.borrow().to_rust_config(py)?;
|
||||
Ok(chanlun::algorithm::segment::线段::_非缺口下穿刺(
|
||||
@@ -873,9 +904,9 @@ impl 线段Py {
|
||||
层级: i64,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<bool> {
|
||||
let mut seg_seq: Vec<Rc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let config = 配置.borrow().to_rust_config(py)?;
|
||||
Ok(chanlun::algorithm::segment::线段::_缺口后紧急修正(
|
||||
@@ -894,9 +925,9 @@ impl 线段Py {
|
||||
层级: i64,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<bool> {
|
||||
let mut seg_seq: Vec<Rc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let config = 配置.borrow().to_rust_config(py)?;
|
||||
Ok(chanlun::algorithm::segment::线段::_修正(
|
||||
@@ -918,13 +949,13 @@ impl 线段Py {
|
||||
关系序列: Option<Vec<相对方向Py>>,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<()> {
|
||||
let bi_list: Vec<Rc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
let bi_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let mut seg_seq: Vec<Rc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let config = 配置.borrow().to_rust_config(py)?;
|
||||
let default_rel = vec![
|
||||
@@ -953,13 +984,13 @@ impl 线段Py {
|
||||
行号: u32,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<()> {
|
||||
let mut seg_seq: Vec<Rc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::segment::线段::_添加扩展线段(
|
||||
&mut seg_seq,
|
||||
Rc::clone(&待添加线段.borrow().inner),
|
||||
Arc::clone(&待添加线段.borrow().inner),
|
||||
行号,
|
||||
);
|
||||
Ok(())
|
||||
@@ -974,13 +1005,13 @@ impl 线段Py {
|
||||
行号: u32,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<Option<虚线Py>> {
|
||||
let mut seg_seq: Vec<Rc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let result = chanlun::algorithm::segment::线段::_弹出扩展线段(
|
||||
&mut seg_seq,
|
||||
&Rc::clone(&待弹出线段.borrow().inner),
|
||||
&Arc::clone(&待弹出线段.borrow().inner),
|
||||
行号,
|
||||
);
|
||||
Ok(result.map(|inner| 虚线Py { inner }))
|
||||
@@ -995,13 +1026,13 @@ impl 线段Py {
|
||||
配置: &Bound<'_, 缠论配置Py>,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<()> {
|
||||
let dash_list: Vec<Rc<chanlun::structure::dash_line::虚线>> = 虚线序列
|
||||
let dash_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 虚线序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let mut seg_seq: Vec<Rc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let config = 配置.borrow().to_rust_config(py)?;
|
||||
chanlun::algorithm::segment::线段::扩展分析(&dash_list, &mut seg_seq, &config);
|
||||
@@ -1034,7 +1065,7 @@ impl 线段Py {
|
||||
let obs_ref = obs.obs();
|
||||
chanlun::algorithm::segment::线段::获取所有停顿位置(&段.borrow().inner, &*obs_ref)
|
||||
.into_iter()
|
||||
.map(|d| 虚线Py { inner: Rc::new(d) })
|
||||
.map(|d| 虚线Py { inner: Arc::new(d) })
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -1044,12 +1075,13 @@ impl 线段Py {
|
||||
_cls: &Bound<'_, PyType>,
|
||||
当前段: &Bound<'_, 虚线Py>,
|
||||
观察员: &Bound<'_, 观察者Py>,
|
||||
) -> Vec<缠论K线Py> {
|
||||
py: Python<'_>,
|
||||
) -> Vec<Py<缠论K线Py>> {
|
||||
let obs = 观察员.borrow();
|
||||
let obs_ref = obs.obs();
|
||||
chanlun::algorithm::segment::线段::是否背驰过(&当前段.borrow().inner, &*obs_ref)
|
||||
.into_iter()
|
||||
.map(|ck| 缠论K线Py::from_rc(ck))
|
||||
.map(|ck| chan_kline_to_py(py, ck))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
@@ -1093,10 +1125,10 @@ impl 线段Py {
|
||||
/// 向中枢序列尾部添加(中枢序列, 新中枢) — 维护中枢序列顺序
|
||||
/// 从中枢序列尾部弹出(中枢序列, 配置) — 弹出最后一个中枢并更新相邻中枢
|
||||
/// 分析(虚线序列, 中枢序列容器, 允许重叠?, 标识前缀?, ...) — 中枢分析主流程
|
||||
#[pyclass(name = "中枢", module = "chanlun._chanlun", unsendable, from_py_object)]
|
||||
#[pyclass(name = "中枢", module = "chanlun._chanlun", from_py_object)]
|
||||
#[derive(Clone)]
|
||||
pub struct 中枢Py {
|
||||
pub(crate) inner: Rc<chanlun::algorithm::hub::中枢>,
|
||||
pub(crate) inner: Arc<chanlun::algorithm::hub::中枢>,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
@@ -1105,12 +1137,12 @@ impl 中枢Py {
|
||||
fn new(
|
||||
序号: i64, 标识: String, 级别: i64, 基础序列: Vec<Py<虚线Py>>, py: Python<'_>
|
||||
) -> Self {
|
||||
let rc_list: Vec<Rc<chanlun::structure::dash_line::虚线>> = 基础序列
|
||||
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 基础序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
Self {
|
||||
inner: Rc::new(chanlun::algorithm::hub::中枢::new(
|
||||
inner: Arc::new(chanlun::algorithm::hub::中枢::new(
|
||||
序号, 标识, 级别, rc_list,
|
||||
)),
|
||||
}
|
||||
@@ -1118,48 +1150,51 @@ impl 中枢Py {
|
||||
|
||||
#[getter]
|
||||
fn 序号(&self) -> i64 {
|
||||
self.inner.序号
|
||||
self.inner.序号.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 标识(&self) -> String {
|
||||
self.inner.标识.clone()
|
||||
self.inner.标识.read().unwrap().clone()
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 级别(&self) -> i64 {
|
||||
self.inner.级别
|
||||
self.inner.级别.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 基础序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for d in &self.inner.基础序列 {
|
||||
list.append(虚线Py {
|
||||
inner: Rc::clone(d),
|
||||
})?;
|
||||
for d in self.inner.基础序列.read().unwrap().iter() {
|
||||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 第三买卖线(&self) -> Option<虚线Py> {
|
||||
self.inner.第三买卖线.as_ref().map(|d| 虚线Py {
|
||||
inner: Rc::clone(d),
|
||||
})
|
||||
fn 第三买卖线(&self, py: Python<'_>) -> Option<Py<虚线Py>> {
|
||||
self.inner
|
||||
.第三买卖线
|
||||
.read()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.map(|d| dashed_to_py(py, Arc::clone(d)))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 本级_第三买卖线(&self) -> Option<虚线Py> {
|
||||
self.inner.本级_第三买卖线.as_ref().map(|d| 虚线Py {
|
||||
inner: Rc::clone(d),
|
||||
})
|
||||
fn 本级_第三买卖线(&self, py: Python<'_>) -> Option<Py<虚线Py>> {
|
||||
self.inner
|
||||
.本级_第三买卖线
|
||||
.read()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.map(|d| dashed_to_py(py, Arc::clone(d)))
|
||||
}
|
||||
|
||||
/// 向中枢添加新虚线(延伸),重置第三买卖线
|
||||
fn 添加虚线(&mut self, 实线: &Bound<'_, 虚线Py>) {
|
||||
let inner = Rc::make_mut(&mut self.inner);
|
||||
inner.添加虚线(Rc::clone(&实线.borrow().inner));
|
||||
fn 添加虚线(&self, 实线: &Bound<'_, 虚线Py>) {
|
||||
self.inner.添加虚线(Arc::clone(&实线.borrow().inner));
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -1170,10 +1205,8 @@ impl 中枢Py {
|
||||
|
||||
#[getter]
|
||||
/// :return: 最后一条虚线
|
||||
fn 离开段(&self) -> 虚线Py {
|
||||
虚线Py {
|
||||
inner: self.inner.离开段(),
|
||||
}
|
||||
fn 离开段(&self, py: Python<'_>) -> Py<虚线Py> {
|
||||
dashed_to_py(py, self.inner.离开段())
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -1210,24 +1243,20 @@ impl 中枢Py {
|
||||
|
||||
#[getter]
|
||||
/// :return: 起点分型
|
||||
fn 文(&self) -> 分型Py {
|
||||
分型Py {
|
||||
inner: self.inner.文(),
|
||||
}
|
||||
fn 文(&self, py: Python<'_>) -> Py<分型Py> {
|
||||
fractal_to_py(py, self.inner.文())
|
||||
}
|
||||
|
||||
#[getter]
|
||||
/// :return: 终点分型
|
||||
fn 武(&self) -> 分型Py {
|
||||
分型Py {
|
||||
inner: self.inner.武(),
|
||||
}
|
||||
fn 武(&self, py: Python<'_>) -> Py<分型Py> {
|
||||
fractal_to_py(py, self.inner.武())
|
||||
}
|
||||
|
||||
/// 设置第三类买卖点关联虚线
|
||||
fn 设置第三买卖线(&mut self, 线: &Bound<'_, 虚线Py>) {
|
||||
let inner = Rc::make_mut(&mut self.inner);
|
||||
inner.设置第三买卖线(Rc::clone(&线.borrow().inner));
|
||||
let inner = Arc::make_mut(&mut self.inner);
|
||||
inner.设置第三买卖线(Arc::clone(&线.borrow().inner));
|
||||
}
|
||||
|
||||
/// 获取中枢的完整虚线序列(基础序列+第三买卖线)
|
||||
@@ -1253,11 +1282,11 @@ impl 中枢Py {
|
||||
py: Python<'_>,
|
||||
) -> bool {
|
||||
let _ = 中枢序列; // Python 版声明了此参数但未使用
|
||||
let rc_list: Vec<Rc<chanlun::structure::dash_line::虚线>> = 序列
|
||||
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let inner = Rc::make_mut(&mut self.inner);
|
||||
let inner = Arc::make_mut(&mut self.inner);
|
||||
inner.校验合法性(&rc_list)
|
||||
}
|
||||
|
||||
@@ -1274,9 +1303,9 @@ impl 中枢Py {
|
||||
配置: &Bound<'_, crate::config_py::缠论配置Py>,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<()> {
|
||||
let mut hub_seq: Vec<Rc<chanlun::algorithm::hub::中枢>> = 扩展中枢
|
||||
let mut hub_seq: Vec<Arc<chanlun::algorithm::hub::中枢>> = 扩展中枢
|
||||
.iter()
|
||||
.map(|h| Rc::clone(&h.bind(py).borrow().inner))
|
||||
.map(|h| Arc::clone(&h.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let config = 配置.borrow().to_rust_config(配置.py())?;
|
||||
self.inner.获取扩展中枢(&mut hub_seq, &config);
|
||||
@@ -1315,13 +1344,13 @@ impl 中枢Py {
|
||||
|
||||
fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
|
||||
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
|
||||
return Rc::as_ptr(&self.inner) == Rc::as_ptr(&other.inner);
|
||||
return Arc::as_ptr(&self.inner) == Arc::as_ptr(&other.inner);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn __hash__(&self) -> u64 {
|
||||
Rc::as_ptr(&self.inner) as u64
|
||||
Arc::as_ptr(&self.inner) as u64
|
||||
}
|
||||
|
||||
// ---- classmethods ----
|
||||
@@ -1353,10 +1382,10 @@ impl 中枢Py {
|
||||
标识: &str,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: Rc::new(chanlun::algorithm::hub::中枢::创建(
|
||||
Rc::clone(&左.borrow().inner),
|
||||
Rc::clone(&中.borrow().inner),
|
||||
Rc::clone(&右.borrow().inner),
|
||||
inner: Arc::new(chanlun::algorithm::hub::中枢::创建(
|
||||
Arc::clone(&左.borrow().inner),
|
||||
Arc::clone(&中.borrow().inner),
|
||||
Arc::clone(&右.borrow().inner),
|
||||
级别,
|
||||
标识,
|
||||
)),
|
||||
@@ -1372,9 +1401,9 @@ impl 中枢Py {
|
||||
标识: &str,
|
||||
py: Python<'_>,
|
||||
) -> Option<Self> {
|
||||
let rc_list: Vec<Rc<chanlun::structure::dash_line::虚线>> = 虚线序列
|
||||
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 虚线序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::hub::中枢::从序列中获取中枢(
|
||||
&rc_list,
|
||||
@@ -1391,7 +1420,7 @@ impl 中枢Py {
|
||||
中枢序列: &Bound<'_, PyAny>,
|
||||
待添加中枢: &Bound<'_, Self>,
|
||||
) -> PyResult<()> {
|
||||
let inner = Rc::clone(&待添加中枢.borrow().inner);
|
||||
let inner = Arc::clone(&待添加中枢.borrow().inner);
|
||||
let wrapper = Py::new(中枢序列.py(), Self { inner })?;
|
||||
中枢序列.call_method1("append", (wrapper,))?;
|
||||
Ok(())
|
||||
@@ -1410,7 +1439,7 @@ impl 中枢Py {
|
||||
}
|
||||
let bound: Bound<'_, Self> = result.extract()?;
|
||||
Ok(Some(Self {
|
||||
inner: Rc::clone(&bound.borrow().inner),
|
||||
inner: Arc::clone(&bound.borrow().inner),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1426,13 +1455,13 @@ impl 中枢Py {
|
||||
层级: i64,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<()> {
|
||||
let rc_list: Vec<Rc<chanlun::structure::dash_line::虚线>> = 虚线序列
|
||||
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 虚线序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let mut hub_seq: Vec<Rc<chanlun::algorithm::hub::中枢>> = 中枢序列
|
||||
let mut hub_seq: Vec<Arc<chanlun::algorithm::hub::中枢>> = 中枢序列
|
||||
.iter()
|
||||
.map(|h| Rc::clone(&h.bind(py).borrow().inner))
|
||||
.map(|h| Arc::clone(&h.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::hub::中枢::分析(&rc_list, &mut hub_seq, 跳过首部, 标识, 层级);
|
||||
Ok(())
|
||||
|
||||
@@ -24,9 +24,13 @@
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyDict, PyType};
|
||||
use std::cell::RefCell;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use crate::algorithm_py::hub_to_py;
|
||||
use crate::kline_py::bar_to_py;
|
||||
use crate::structure_py::{dashed_to_py, fractal_to_py};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::algorithm_py::中枢Py;
|
||||
use crate::config_py::缠论配置Py;
|
||||
@@ -44,12 +48,7 @@ use crate::types_py::买卖点类型Py;
|
||||
/// 有效性: bool — 买卖点是否仍有效
|
||||
/// 与MACD柱子匹配: bool|None — 是否与MACD柱状图方向匹配
|
||||
/// 与MACD柱子分型匹配: bool|None — 是否与MACD柱分型匹配
|
||||
#[pyclass(
|
||||
name = "基础买卖点",
|
||||
module = "chanlun._chanlun",
|
||||
unsendable,
|
||||
from_py_object
|
||||
)]
|
||||
#[pyclass(name = "基础买卖点", module = "chanlun._chanlun", from_py_object)]
|
||||
#[derive(Clone)]
|
||||
pub struct 基础买卖点Py {
|
||||
pub(crate) inner: chanlun::business::bsp::基础买卖点,
|
||||
@@ -69,7 +68,7 @@ impl 基础买卖点Py {
|
||||
inner: chanlun::business::bsp::基础买卖点::new(
|
||||
类型.borrow().inner,
|
||||
当前K线.borrow().inner.clone(),
|
||||
Rc::clone(&买卖点分型.borrow().inner),
|
||||
Arc::clone(&买卖点分型.borrow().inner),
|
||||
备注,
|
||||
中枢破位值,
|
||||
),
|
||||
@@ -97,35 +96,35 @@ impl 基础买卖点Py {
|
||||
#[getter]
|
||||
fn 买卖点分型(&self) -> 分型Py {
|
||||
分型Py {
|
||||
inner: Rc::clone(&self.inner.买卖点分型),
|
||||
inner: Arc::clone(&self.inner.买卖点分型),
|
||||
}
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 买卖点K线(&self) -> 缠论K线Py {
|
||||
缠论K线Py::from_rc(Rc::clone(&self.inner.买卖点K线))
|
||||
fn 买卖点K线(&self, py: Python<'_>) -> Py<缠论K线Py> {
|
||||
crate::kline_py::chan_kline_to_py(py, Arc::clone(&self.inner.买卖点K线))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
/// 当前K线
|
||||
fn 当前K线(&self) -> K线Py {
|
||||
K线Py {
|
||||
inner: self.inner.当前K线.clone(),
|
||||
}
|
||||
fn 当前K线(&self, py: Python<'_>) -> Py<K线Py> {
|
||||
bar_to_py(py, self.inner.当前K线.clone())
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 失效K线(&self) -> Option<K线Py> {
|
||||
self.inner.失效K线.as_ref().map(|k| K线Py {
|
||||
inner: Rc::clone(k),
|
||||
})
|
||||
fn 失效K线(&self, py: Python<'_>) -> Option<Py<K线Py>> {
|
||||
self.inner
|
||||
.失效K线
|
||||
.as_ref()
|
||||
.map(|k| bar_to_py(py, Arc::clone(k)))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 终结K线(&self) -> Option<K线Py> {
|
||||
self.inner.终结K线.as_ref().map(|k| K线Py {
|
||||
inner: Rc::clone(k),
|
||||
})
|
||||
fn 终结K线(&self, py: Python<'_>) -> Option<Py<K线Py>> {
|
||||
self.inner
|
||||
.终结K线
|
||||
.as_ref()
|
||||
.map(|k| bar_to_py(py, Arc::clone(k)))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -223,7 +222,7 @@ impl 买卖点Py {
|
||||
) -> 基础买卖点Py {
|
||||
基础买卖点Py {
|
||||
inner: chanlun::business::bsp::买卖点::一卖点(
|
||||
Rc::clone(&买卖点分型.borrow().inner),
|
||||
Arc::clone(&买卖点分型.borrow().inner),
|
||||
当前K线.borrow().inner.clone(),
|
||||
标识,
|
||||
备注,
|
||||
@@ -244,7 +243,7 @@ impl 买卖点Py {
|
||||
) -> 基础买卖点Py {
|
||||
基础买卖点Py {
|
||||
inner: chanlun::business::bsp::买卖点::一买点(
|
||||
Rc::clone(&买卖点分型.borrow().inner),
|
||||
Arc::clone(&买卖点分型.borrow().inner),
|
||||
当前K线.borrow().inner.clone(),
|
||||
标识,
|
||||
备注,
|
||||
@@ -265,7 +264,7 @@ impl 买卖点Py {
|
||||
) -> 基础买卖点Py {
|
||||
基础买卖点Py {
|
||||
inner: chanlun::business::bsp::买卖点::二卖点(
|
||||
Rc::clone(&买卖点分型.borrow().inner),
|
||||
Arc::clone(&买卖点分型.borrow().inner),
|
||||
当前K线.borrow().inner.clone(),
|
||||
标识,
|
||||
备注,
|
||||
@@ -286,7 +285,7 @@ impl 买卖点Py {
|
||||
) -> 基础买卖点Py {
|
||||
基础买卖点Py {
|
||||
inner: chanlun::business::bsp::买卖点::二买点(
|
||||
Rc::clone(&买卖点分型.borrow().inner),
|
||||
Arc::clone(&买卖点分型.borrow().inner),
|
||||
当前K线.borrow().inner.clone(),
|
||||
标识,
|
||||
备注,
|
||||
@@ -307,7 +306,7 @@ impl 买卖点Py {
|
||||
) -> 基础买卖点Py {
|
||||
基础买卖点Py {
|
||||
inner: chanlun::business::bsp::买卖点::三卖点(
|
||||
Rc::clone(&买卖点分型.borrow().inner),
|
||||
Arc::clone(&买卖点分型.borrow().inner),
|
||||
当前K线.borrow().inner.clone(),
|
||||
标识,
|
||||
备注,
|
||||
@@ -328,7 +327,7 @@ impl 买卖点Py {
|
||||
) -> 基础买卖点Py {
|
||||
基础买卖点Py {
|
||||
inner: chanlun::business::bsp::买卖点::三买点(
|
||||
Rc::clone(&买卖点分型.borrow().inner),
|
||||
Arc::clone(&买卖点分型.borrow().inner),
|
||||
当前K线.borrow().inner.clone(),
|
||||
标识,
|
||||
备注,
|
||||
@@ -352,8 +351,8 @@ impl 买卖点Py {
|
||||
特征,
|
||||
序号,
|
||||
级别,
|
||||
Rc::clone(&买卖点分型.borrow().inner),
|
||||
Rc::clone(&当前缠K.borrow().inner),
|
||||
Arc::clone(&买卖点分型.borrow().inner),
|
||||
Arc::clone(&当前缠K.borrow().inner),
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -390,24 +389,30 @@ impl 买卖点Py {
|
||||
/// 调试方法:
|
||||
/// 测试_保存数据(root?) — 将各层级序列保存到文件,用于与Python版对比
|
||||
/// 读取数据文件(文件路径, 配置) -> 观察者 (classmethod) — 从 .nb 文件加载数据
|
||||
#[pyclass(name = "观察者", module = "chanlun._chanlun", subclass, unsendable)]
|
||||
#[pyclass(name = "观察者", module = "chanlun._chanlun", subclass)]
|
||||
pub struct 观察者Py {
|
||||
pub(crate) inner: Option<Rc<RefCell<chanlun::business::observer::观察者>>>,
|
||||
pub(crate) inner: Option<Arc<RwLock<chanlun::business::observer::观察者>>>,
|
||||
}
|
||||
|
||||
impl 观察者Py {
|
||||
pub(crate) fn obs(&self) -> std::cell::Ref<'_, chanlun::business::observer::观察者> {
|
||||
pub(crate) fn obs(
|
||||
&self,
|
||||
) -> std::sync::RwLockReadGuard<'_, chanlun::business::observer::观察者> {
|
||||
self.inner
|
||||
.as_ref()
|
||||
.expect("观察者 尚未初始化,请通过 __init__(符号, 周期, 配置) 构造")
|
||||
.borrow()
|
||||
.read()
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
}
|
||||
|
||||
pub(crate) fn obs_mut(&self) -> std::cell::RefMut<'_, chanlun::business::observer::观察者> {
|
||||
pub(crate) fn obs_mut(
|
||||
&self,
|
||||
) -> std::sync::RwLockWriteGuard<'_, chanlun::business::observer::观察者> {
|
||||
self.inner
|
||||
.as_ref()
|
||||
.expect("观察者 尚未初始化,请通过 __init__(符号, 周期, 配置) 构造")
|
||||
.borrow_mut()
|
||||
.write()
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,18 +497,16 @@ impl 观察者Py {
|
||||
|
||||
#[getter]
|
||||
/// :return: 最后一根原始K线
|
||||
fn 当前K线(&self) -> Option<K线Py> {
|
||||
self.obs().当前K线().map(|k| K线Py {
|
||||
inner: Rc::clone(k),
|
||||
})
|
||||
fn 当前K线(&self, py: Python<'_>) -> Option<Py<K线Py>> {
|
||||
self.obs().当前K线().map(|k| bar_to_py(py, Arc::clone(k)))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
/// :return: 最后一根缠论K线
|
||||
fn 当前缠K(&self) -> Option<缠论K线Py> {
|
||||
fn 当前缠K(&self, py: Python<'_>) -> Option<Py<缠论K线Py>> {
|
||||
self.obs()
|
||||
.当前缠K()
|
||||
.map(|k| 缠论K线Py::from_rc(Rc::clone(k)))
|
||||
.map(|k| crate::kline_py::chan_kline_to_py(py, Arc::clone(k)))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -551,7 +554,7 @@ impl 观察者Py {
|
||||
let k线_py = Py::new(
|
||||
py,
|
||||
K线Py {
|
||||
inner: Rc::new(k线),
|
||||
inner: Arc::new(k线),
|
||||
},
|
||||
)?;
|
||||
slf.call_method1("增加原始K线", (k线_py,))?;
|
||||
@@ -564,6 +567,7 @@ impl 观察者Py {
|
||||
self.obs_mut().静态重新分析();
|
||||
}
|
||||
|
||||
#[pyo3(signature = (root = None))]
|
||||
/// 拆分各序列数据,单独存文件,文件名为对应变量名
|
||||
fn 测试_保存数据(&self, root: Option<&str>) {
|
||||
self.obs().测试_保存数据(root);
|
||||
@@ -618,7 +622,7 @@ impl 观察者Py {
|
||||
let k线_py = Py::new(
|
||||
py,
|
||||
K线Py {
|
||||
inner: Rc::new(k线),
|
||||
inner: Arc::new(k线),
|
||||
},
|
||||
)?;
|
||||
obj.call_method1("增加原始K线", (k线_py,))?;
|
||||
@@ -634,18 +638,38 @@ impl 观察者Py {
|
||||
fn 普通K线序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for k in &self.obs().普通K线序列 {
|
||||
list.append(K线Py {
|
||||
inner: Rc::clone(k),
|
||||
})?;
|
||||
list.append(bar_to_py(py, Arc::clone(k)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 基础缠K序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for k in &self.obs().基础缠K序列 {
|
||||
list.append(crate::kline_py::chan_kline_to_py(py, Arc::clone(k)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
|
||||
#[setter]
|
||||
#[pyo3(name = "基础缠K序列")]
|
||||
fn 设置_基础缠K序列(&mut self, value: &Bound<'_, PyAny>) -> PyResult<()> {
|
||||
let list: &Bound<'_, pyo3::types::PyList> = value.cast()?;
|
||||
let mut vec = Vec::new();
|
||||
for item in list {
|
||||
let 缠k: PyRef<'_, 缠论K线Py> = item.extract()?;
|
||||
vec.push(Arc::clone(&缠k.inner));
|
||||
}
|
||||
self.obs_mut().基础缠K序列 = vec;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 缠论K线序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for k in &self.obs().缠论K线序列 {
|
||||
list.append(缠论K线Py::from_rc(Rc::clone(k)))?;
|
||||
list.append(crate::kline_py::chan_kline_to_py(py, Arc::clone(k)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -654,9 +678,7 @@ impl 观察者Py {
|
||||
fn 分型序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for f in &self.obs().分型序列 {
|
||||
list.append(分型Py {
|
||||
inner: Rc::clone(f),
|
||||
})?;
|
||||
list.append(fractal_to_py(py, Arc::clone(f)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -665,9 +687,7 @@ impl 观察者Py {
|
||||
fn 笔序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for d in &self.obs().笔序列 {
|
||||
list.append(虚线Py {
|
||||
inner: Rc::clone(d),
|
||||
})?;
|
||||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -676,9 +696,7 @@ impl 观察者Py {
|
||||
fn 笔_中枢序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for h in &self.obs().笔_中枢序列 {
|
||||
list.append(中枢Py {
|
||||
inner: Rc::clone(h),
|
||||
})?;
|
||||
list.append(hub_to_py(py, Arc::clone(h)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -687,9 +705,7 @@ impl 观察者Py {
|
||||
fn 线段序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for d in &self.obs().线段序列 {
|
||||
list.append(虚线Py {
|
||||
inner: Rc::clone(d),
|
||||
})?;
|
||||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -698,9 +714,7 @@ impl 观察者Py {
|
||||
fn 中枢序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for h in &self.obs().中枢序列 {
|
||||
list.append(中枢Py {
|
||||
inner: Rc::clone(h),
|
||||
})?;
|
||||
list.append(hub_to_py(py, Arc::clone(h)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -709,9 +723,7 @@ impl 观察者Py {
|
||||
fn 扩展线段序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for d in &self.obs().扩展线段序列 {
|
||||
list.append(虚线Py {
|
||||
inner: Rc::clone(d),
|
||||
})?;
|
||||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -720,9 +732,7 @@ impl 观察者Py {
|
||||
fn 扩展中枢序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for h in &self.obs().扩展中枢序列 {
|
||||
list.append(中枢Py {
|
||||
inner: Rc::clone(h),
|
||||
})?;
|
||||
list.append(hub_to_py(py, Arc::clone(h)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -731,9 +741,7 @@ impl 观察者Py {
|
||||
fn 扩展线段序列_线段(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for d in &self.obs().扩展线段序列_线段 {
|
||||
list.append(虚线Py {
|
||||
inner: Rc::clone(d),
|
||||
})?;
|
||||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -742,9 +750,7 @@ impl 观察者Py {
|
||||
fn 扩展中枢序列_线段(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for h in &self.obs().扩展中枢序列_线段 {
|
||||
list.append(中枢Py {
|
||||
inner: Rc::clone(h),
|
||||
})?;
|
||||
list.append(hub_to_py(py, Arc::clone(h)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -753,9 +759,7 @@ impl 观察者Py {
|
||||
fn 线段_线段序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for d in &self.obs().线段_线段序列 {
|
||||
list.append(虚线Py {
|
||||
inner: Rc::clone(d),
|
||||
})?;
|
||||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -764,9 +768,7 @@ impl 观察者Py {
|
||||
fn 线段_中枢序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for h in &self.obs().线段_中枢序列 {
|
||||
list.append(中枢Py {
|
||||
inner: Rc::clone(h),
|
||||
})?;
|
||||
list.append(hub_to_py(py, Arc::clone(h)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -775,9 +777,7 @@ impl 观察者Py {
|
||||
fn 扩展线段序列_扩展线段(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for d in &self.obs().扩展线段序列_扩展线段 {
|
||||
list.append(虚线Py {
|
||||
inner: Rc::clone(d),
|
||||
})?;
|
||||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -786,9 +786,7 @@ impl 观察者Py {
|
||||
fn 扩展中枢序列_扩展线段(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for h in &self.obs().扩展中枢序列_扩展线段 {
|
||||
list.append(中枢Py {
|
||||
inner: Rc::clone(h),
|
||||
})?;
|
||||
list.append(hub_to_py(py, Arc::clone(h)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -806,7 +804,7 @@ impl 观察者Py {
|
||||
/// 投喂(时间戳, 开盘价, 最高价, 最低价, 收盘价, 成交量) -> list[(周期, K线)]
|
||||
/// — 快捷入口,免去构造K线对象
|
||||
/// 获取当前K线(周期) -> K线|None — 获取指定周期的当前合成结果
|
||||
#[pyclass(name = "K线合成器", module = "chanlun._chanlun", unsendable)]
|
||||
#[pyclass(name = "K线合成器", module = "chanlun._chanlun")]
|
||||
pub struct K线合成器Py {
|
||||
pub(crate) inner: chanlun::business::synthesizer::K线合成器,
|
||||
}
|
||||
@@ -829,7 +827,7 @@ impl K线合成器Py {
|
||||
let results = self.inner.投喂K线((*普K.borrow().inner).clone());
|
||||
Ok(results
|
||||
.into_iter()
|
||||
.map(|(周期, k)| (周期, K线Py { inner: Rc::new(k) }))
|
||||
.map(|(周期, k)| (周期, K线Py { inner: Arc::new(k) }))
|
||||
.collect())
|
||||
}
|
||||
|
||||
@@ -858,14 +856,21 @@ impl K线合成器Py {
|
||||
let results = self.inner.投喂K线(k);
|
||||
results
|
||||
.into_iter()
|
||||
.map(|(周期, k2)| (周期, K线Py { inner: Rc::new(k2) }))
|
||||
.map(|(周期, k2)| {
|
||||
(
|
||||
周期,
|
||||
K线Py {
|
||||
inner: Arc::new(k2),
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// 获取指定周期当前正在合成的K线
|
||||
fn 获取当前K线(&self, 周期: i64) -> Option<K线Py> {
|
||||
self.inner.获取当前K线(周期).map(|k| K线Py {
|
||||
inner: Rc::new(k.clone()),
|
||||
inner: Arc::new(k.clone()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -893,7 +898,7 @@ impl K线合成器Py {
|
||||
/// 方法:
|
||||
/// 投喂K线(普K) — 喂入最小周期K线,自动合成大周期并分发给各周期观察者
|
||||
/// 测试_保存数据(root?) — 保存各周期的分析数据到文件
|
||||
#[pyclass(name = "立体分析器", module = "chanlun._chanlun", unsendable)]
|
||||
#[pyclass(name = "立体分析器", module = "chanlun._chanlun")]
|
||||
pub struct 立体分析器Py {
|
||||
pub(crate) inner: chanlun::business::multi_frame::立体分析器,
|
||||
}
|
||||
|
||||
@@ -183,20 +183,16 @@ impl 缠论配置Py {
|
||||
}
|
||||
|
||||
/// 保存配置到 JSON 文件(默认路径 "缠论配置.json")。
|
||||
fn 保存配置(&self, py: Python<'_>, path: Option<&str>) -> PyResult<()> {
|
||||
let path = path.unwrap_or("缠论配置.json");
|
||||
#[pyo3(signature = (path = "缠论配置.json"))]
|
||||
fn 保存配置(&self, py: Python<'_>, path: &str) -> PyResult<()> {
|
||||
let json = self.to_json(py)?;
|
||||
std::fs::write(path, json).map_err(|e| pyo3::exceptions::PyIOError::new_err(e.to_string()))
|
||||
}
|
||||
|
||||
/// 从 JSON 文件加载配置(默认路径 "缠论配置.json")。
|
||||
#[classmethod]
|
||||
fn 加载配置(
|
||||
_cls: &Bound<'_, PyType>,
|
||||
py: Python<'_>,
|
||||
path: Option<&str>,
|
||||
) -> PyResult<Self> {
|
||||
let path = path.unwrap_or("缠论配置.json");
|
||||
#[pyo3(signature = (path = "缠论配置.json"))]
|
||||
fn 加载配置(_cls: &Bound<'_, PyType>, py: Python<'_>, path: &str) -> PyResult<Self> {
|
||||
let json_str = std::fs::read_to_string(path)
|
||||
.map_err(|e| pyo3::exceptions::PyIOError::new_err(e.to_string()))?;
|
||||
Self::from_json_str(py, &json_str)
|
||||
@@ -336,11 +332,104 @@ fn dict_to_rust_config(
|
||||
let mut value: serde_json::Value = serde_json::from_str(&json_str)
|
||||
.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("配置转换失败: {e}")))?;
|
||||
coerce_strings_to_numbers(&mut value);
|
||||
serde_json::from_value(value)
|
||||
|
||||
// 获取默认配置的 JSON 表示作为 schema
|
||||
let default_config = chanlun::config::缠论配置::default();
|
||||
let default_json = serde_json::to_value(&default_config)
|
||||
.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("配置转换失败: {e}")))?;
|
||||
|
||||
// 用默认值做基准,只合并类型匹配的字段
|
||||
let mut merged = default_json.clone();
|
||||
if let serde_json::Value::Object(ref input_map) = value {
|
||||
if let serde_json::Value::Object(ref default_map) = default_json {
|
||||
for (key, input_val) in input_map {
|
||||
if let Some(default_val) = default_map.get(key) {
|
||||
match validate_field(key, input_val, default_val) {
|
||||
Ok(()) => {
|
||||
merged[key] = input_val.clone();
|
||||
}
|
||||
Err(msg) => {
|
||||
eprintln!("\x1b[33m[配置警告]\x1b[m {key}: {msg},已使用默认值 {default_val}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serde_json::from_value(merged)
|
||||
.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("配置转换失败: {e}")))
|
||||
})
|
||||
}
|
||||
|
||||
/// 字符串字段的有效值集合
|
||||
fn valid_string_values(field: &str) -> Option<&'static [&'static str]> {
|
||||
match field {
|
||||
"指标计算方式" => Some(&[
|
||||
"开",
|
||||
"高",
|
||||
"低",
|
||||
"收",
|
||||
"高低均值",
|
||||
"高低收均值",
|
||||
"开高低收均值",
|
||||
]),
|
||||
"买卖点_指标模式" => Some(&["任意", "配置", "全量", "相对"]),
|
||||
"线段内部背驰_模式" => Some(&["任意", "配置", "全量", "相对"]),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 验证单个字段的值是否与默认值类型兼容
|
||||
fn validate_field(
|
||||
key: &str,
|
||||
input: &serde_json::Value,
|
||||
default: &serde_json::Value,
|
||||
) -> Result<(), String> {
|
||||
use serde_json::Value;
|
||||
|
||||
// 输入为 null → 跳过(保留默认)
|
||||
if input.is_null() {
|
||||
return Err("值为 null".into());
|
||||
}
|
||||
|
||||
// 字符串字段:检查有效值白名单
|
||||
if default.is_string() && input.is_string() {
|
||||
if let Some(valid) = valid_string_values(key) {
|
||||
let s = input.as_str().unwrap();
|
||||
if !valid.contains(&s) {
|
||||
return Err(format!("\"{s}\" 不在有效值 {valid:?} 内"));
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// 类型匹配:直接通过
|
||||
match (default, input) {
|
||||
(Value::Bool(_), Value::Bool(_)) => return Ok(()),
|
||||
(Value::Number(_), Value::Number(_)) => return Ok(()),
|
||||
(Value::String(_), Value::String(_)) => return Ok(()),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// 类型不匹配
|
||||
let type_name = match input {
|
||||
Value::Bool(_) => "布尔",
|
||||
Value::Number(_) => "数值",
|
||||
Value::String(_) => "字符串",
|
||||
Value::Array(_) => "数组",
|
||||
Value::Object(_) => "字典",
|
||||
Value::Null => "null",
|
||||
};
|
||||
let expected = match default {
|
||||
Value::Bool(_) => "布尔",
|
||||
Value::Number(_) => "数值",
|
||||
Value::String(_) => "字符串",
|
||||
_ => "其他",
|
||||
};
|
||||
Err(format!("类型不匹配(需要 {expected},收到 {type_name})"))
|
||||
}
|
||||
|
||||
/// 递归遍历 JSON Value,将数字/布尔字符串转为对应类型。
|
||||
fn coerce_strings_to_numbers(value: &mut serde_json::Value) {
|
||||
match value {
|
||||
|
||||
+104
-94
@@ -25,7 +25,9 @@
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyBytes, PyDict, PyType};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use crate::config_py::缠论配置Py;
|
||||
use crate::indicators_py::{平滑异同移动平均线Py, 相对强弱指数Py, 随机指标Py};
|
||||
@@ -52,9 +54,9 @@ use crate::types_py::相对方向Py;
|
||||
/// 获取MACD(K线序列, 计算方式, 快线周期?, 慢线周期?, 信号周期?) -> list[平滑异同移动平均线]
|
||||
/// — 对整个K线序列批量计算 MACD
|
||||
/// 截取(序列, 起点K线, 终点K线) -> list — 按时间戳截取K线区间
|
||||
#[pyclass(name = "K线", module = "chanlun._chanlun", unsendable)]
|
||||
#[pyclass(name = "K线", module = "chanlun._chanlun")]
|
||||
pub struct K线Py {
|
||||
pub(crate) inner: Rc<chanlun::kline::bar::K线>,
|
||||
pub(crate) inner: Arc<chanlun::kline::bar::K线>,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
@@ -73,7 +75,7 @@ impl K线Py {
|
||||
成交量: f64,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: Rc::new(chanlun::kline::bar::K线 {
|
||||
inner: Arc::new(chanlun::kline::bar::K线 {
|
||||
标识: 标识.to_string(),
|
||||
序号,
|
||||
周期,
|
||||
@@ -94,82 +96,46 @@ impl K线Py {
|
||||
fn 标识(&self) -> String {
|
||||
self.inner.标识.clone()
|
||||
}
|
||||
#[setter]
|
||||
fn set_标识(&mut self, v: String) {
|
||||
Rc::make_mut(&mut self.inner).标识 = v;
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 序号(&self) -> i64 {
|
||||
self.inner.序号
|
||||
}
|
||||
#[setter]
|
||||
fn set_序号(&mut self, v: i64) {
|
||||
Rc::make_mut(&mut self.inner).序号 = v;
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 周期(&self) -> i64 {
|
||||
self.inner.周期
|
||||
}
|
||||
#[setter]
|
||||
fn set_周期(&mut self, v: i64) {
|
||||
Rc::make_mut(&mut self.inner).周期 = v;
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 时间戳(&self) -> i64 {
|
||||
self.inner.时间戳
|
||||
}
|
||||
#[setter]
|
||||
fn set_时间戳(&mut self, v: i64) {
|
||||
Rc::make_mut(&mut self.inner).时间戳 = v;
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 高(&self) -> f64 {
|
||||
self.inner.高
|
||||
}
|
||||
#[setter]
|
||||
fn set_高(&mut self, v: f64) {
|
||||
Rc::make_mut(&mut self.inner).高 = v;
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 低(&self) -> f64 {
|
||||
self.inner.低
|
||||
}
|
||||
#[setter]
|
||||
fn set_低(&mut self, v: f64) {
|
||||
Rc::make_mut(&mut self.inner).低 = v;
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 开盘价(&self) -> f64 {
|
||||
self.inner.开盘价
|
||||
}
|
||||
#[setter]
|
||||
fn set_开盘价(&mut self, v: f64) {
|
||||
Rc::make_mut(&mut self.inner).开盘价 = v;
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 收盘价(&self) -> f64 {
|
||||
self.inner.收盘价
|
||||
}
|
||||
#[setter]
|
||||
fn set_收盘价(&mut self, v: f64) {
|
||||
Rc::make_mut(&mut self.inner).收盘价 = v;
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 成交量(&self) -> f64 {
|
||||
self.inner.成交量
|
||||
}
|
||||
#[setter]
|
||||
fn set_成交量(&mut self, v: f64) {
|
||||
Rc::make_mut(&mut self.inner).成交量 = v;
|
||||
}
|
||||
|
||||
#[getter]
|
||||
/// :return: 相对方向.向上(开盘<收盘)或 相对方向.向下(开盘>收盘)
|
||||
@@ -243,13 +209,13 @@ impl K线Py {
|
||||
|
||||
fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
|
||||
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
|
||||
return Rc::as_ptr(&self.inner) == Rc::as_ptr(&other.inner);
|
||||
return Arc::as_ptr(&self.inner) == Arc::as_ptr(&other.inner);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn __hash__(&self) -> u64 {
|
||||
Rc::as_ptr(&self.inner) as u64
|
||||
Arc::as_ptr(&self.inner) as u64
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
@@ -268,7 +234,7 @@ impl K线Py {
|
||||
周期: Option<i64>,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: Rc::new(chanlun::kline::bar::K线::创建普K(
|
||||
inner: Arc::new(chanlun::kline::bar::K线::创建普K(
|
||||
标识,
|
||||
时间戳,
|
||||
开盘价,
|
||||
@@ -306,7 +272,7 @@ impl K线Py {
|
||||
) -> Option<Self> {
|
||||
chanlun::kline::bar::K线::读取大端字节数组(字节组.as_bytes(), 周期, 标识).map(|inner| {
|
||||
Self {
|
||||
inner: Rc::new(inner),
|
||||
inner: Arc::new(inner),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -333,21 +299,21 @@ impl K线Py {
|
||||
终: &Bound<'_, Self>,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<Vec<Py<Self>>> {
|
||||
let start_ptr = Rc::as_ptr(&始.borrow().inner);
|
||||
let end_ptr = Rc::as_ptr(&终.borrow().inner);
|
||||
let start_ptr = Arc::as_ptr(&始.borrow().inner);
|
||||
let end_ptr = Arc::as_ptr(&终.borrow().inner);
|
||||
let start_ts = 始.borrow().inner.时间戳;
|
||||
let end_ts = 终.borrow().inner.时间戳;
|
||||
let start_idx = 序列
|
||||
.iter()
|
||||
.position(|k| {
|
||||
Rc::as_ptr(&k.borrow(py).inner) == start_ptr
|
||||
Arc::as_ptr(&k.borrow(py).inner) == start_ptr
|
||||
|| k.borrow(py).inner.时间戳 == start_ts
|
||||
})
|
||||
.ok_or_else(|| pyo3::exceptions::PyValueError::new_err("始 不在序列中"))?;
|
||||
let end_idx = 序列
|
||||
.iter()
|
||||
.position(|k| {
|
||||
Rc::as_ptr(&k.borrow(py).inner) == end_ptr || k.borrow(py).inner.时间戳 == end_ts
|
||||
Arc::as_ptr(&k.borrow(py).inner) == end_ptr || k.borrow(py).inner.时间戳 == end_ts
|
||||
})
|
||||
.ok_or_else(|| pyo3::exceptions::PyValueError::new_err("终 不在序列中"))?;
|
||||
if start_idx > end_idx {
|
||||
@@ -380,31 +346,71 @@ impl K线Py {
|
||||
/// 分析(缠K序列, 配置, 可以逆序包含?, 忽视顺序包含?, 可以逆序包含新?) -> (str, 分型|None)
|
||||
/// — 分析分型形成结果
|
||||
/// 截取(序列, 起点分型, 终点分型) -> list — 截取分型间的缠K子序列
|
||||
#[pyclass(
|
||||
name = "缠论K线",
|
||||
module = "chanlun._chanlun",
|
||||
unsendable,
|
||||
from_py_object
|
||||
)]
|
||||
#[pyclass(name = "缠论K线", module = "chanlun._chanlun", from_py_object)]
|
||||
pub struct 缠论K线Py {
|
||||
pub(crate) inner: std::rc::Rc<chanlun::kline::chan_kline::缠论K线>,
|
||||
bsp_set: std::cell::RefCell<Option<Py<pyo3::types::PySet>>>,
|
||||
pub(crate) inner: std::sync::Arc<chanlun::kline::chan_kline::缠论K线>,
|
||||
bsp_set: std::sync::RwLock<Option<Py<pyo3::types::PySet>>>,
|
||||
}
|
||||
|
||||
impl 缠论K线Py {
|
||||
pub(crate) fn from_rc(inner: std::rc::Rc<chanlun::kline::chan_kline::缠论K线>) -> Self {
|
||||
pub(crate) fn from_rc(inner: std::sync::Arc<chanlun::kline::chan_kline::缠论K线>) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
bsp_set: std::cell::RefCell::new(None),
|
||||
bsp_set: std::sync::RwLock::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
/// 对象标识缓存:Rc 地址 → 规范 Python 对象
|
||||
/// 确保同一底层 Rc 指针在 Python 侧始终映射到同一 PyObject
|
||||
|
||||
static BAR_IDENTITY: RwLock<HashMap<usize, Py<K线Py>>> = RwLock::new(HashMap::new());
|
||||
|
||||
static KLINE_IDENTITY: RwLock<HashMap<usize, Py<缠论K线Py>>> = RwLock::new(HashMap::new());
|
||||
}
|
||||
|
||||
/// 将 Rc<K线> 转为 Py<K线Py>,确保同一 Rc 地址总是返回同一 Python 对象
|
||||
pub(crate) fn bar_to_py(
|
||||
py: Python<'_>,
|
||||
inner: std::sync::Arc<chanlun::kline::bar::K线>,
|
||||
) -> Py<K线Py> {
|
||||
let key = Arc::as_ptr(&inner) as usize;
|
||||
if let Some(cached) =
|
||||
BAR_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
let obj = Py::new(py, K线Py { inner }).unwrap();
|
||||
BAR_IDENTITY.with(|c| {
|
||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
||||
});
|
||||
obj
|
||||
}
|
||||
|
||||
/// 将 Rc<缠论K线> 转为 Py<缠论K线Py>,确保同一 Rc 地址总是返回同一 Python 对象
|
||||
pub(crate) fn chan_kline_to_py(
|
||||
py: Python<'_>,
|
||||
inner: std::sync::Arc<chanlun::kline::chan_kline::缠论K线>,
|
||||
) -> Py<缠论K线Py> {
|
||||
let key = Arc::as_ptr(&inner) as usize;
|
||||
if let Some(cached) =
|
||||
KLINE_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
let obj = Py::new(py, 缠论K线Py::from_rc(inner)).unwrap();
|
||||
KLINE_IDENTITY.with(|c| {
|
||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
||||
});
|
||||
obj
|
||||
}
|
||||
|
||||
impl Clone for 缠论K线Py {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: std::rc::Rc::clone(&self.inner),
|
||||
bsp_set: std::cell::RefCell::new(None),
|
||||
inner: std::sync::Arc::clone(&self.inner),
|
||||
bsp_set: std::sync::RwLock::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -418,28 +424,28 @@ impl 缠论K线Py {
|
||||
|
||||
#[getter]
|
||||
fn 序号(&self) -> i64 {
|
||||
self.inner.序号
|
||||
self.inner.序号.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 时间戳(&self) -> i64 {
|
||||
self.inner.时间戳
|
||||
self.inner.时间戳.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 高(&self) -> f64 {
|
||||
self.inner.高
|
||||
self.inner.高.get()
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 低(&self) -> f64 {
|
||||
self.inner.低
|
||||
self.inner.低.get()
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 方向(&self) -> 相对方向Py {
|
||||
相对方向Py {
|
||||
inner: self.inner.方向,
|
||||
inner: *self.inner.方向.read().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,6 +453,8 @@ impl 缠论K线Py {
|
||||
fn 分型(&self) -> Option<crate::types_py::分型结构Py> {
|
||||
self.inner
|
||||
.分型
|
||||
.read()
|
||||
.unwrap()
|
||||
.map(|f| crate::types_py::分型结构Py { inner: f })
|
||||
}
|
||||
|
||||
@@ -462,7 +470,7 @@ impl 缠论K线Py {
|
||||
|
||||
#[getter]
|
||||
fn 分型特征值(&self) -> f64 {
|
||||
self.inner.分型特征值
|
||||
self.inner.分型特征值.get()
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -472,14 +480,12 @@ impl 缠论K线Py {
|
||||
|
||||
#[getter]
|
||||
fn 原始结束序号(&self) -> i64 {
|
||||
self.inner.原始结束序号
|
||||
self.inner.原始结束序号.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 标的K线(&self) -> K线Py {
|
||||
K线Py {
|
||||
inner: self.inner.标的K线.clone(),
|
||||
}
|
||||
fn 标的K线(&self, py: Python<'_>) -> Py<K线Py> {
|
||||
bar_to_py(py, self.inner.标的K线.read().unwrap().clone())
|
||||
}
|
||||
|
||||
/// pandas 兼容 — 返回所有字段构成的字典
|
||||
@@ -516,28 +522,28 @@ impl 缠论K线Py {
|
||||
|
||||
fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
|
||||
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
|
||||
return Rc::as_ptr(&self.inner) == Rc::as_ptr(&other.inner);
|
||||
return Arc::as_ptr(&self.inner) == Arc::as_ptr(&other.inner);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn __hash__(&self) -> u64 {
|
||||
Rc::as_ptr(&self.inner) as u64
|
||||
Arc::as_ptr(&self.inner) as u64
|
||||
}
|
||||
|
||||
#[getter]
|
||||
/// 创建当前缠K的浅拷贝副本
|
||||
fn 镜像(&self, py: Python<'_>) -> Self {
|
||||
let mut mirror = Self {
|
||||
inner: std::rc::Rc::new(self.inner.镜像()),
|
||||
bsp_set: std::cell::RefCell::new(None),
|
||||
inner: std::sync::Arc::new(self.inner.镜像()),
|
||||
bsp_set: std::sync::RwLock::new(None),
|
||||
};
|
||||
if let Some(ref src_set) = *self.bsp_set.borrow() {
|
||||
if let Some(ref src_set) = *self.bsp_set.read().unwrap() {
|
||||
if let Ok(new_set) = pyo3::types::PySet::empty(py) {
|
||||
for item in src_set.bind(py).iter() {
|
||||
let _ = new_set.add(item);
|
||||
}
|
||||
mirror.bsp_set = std::cell::RefCell::new(Some(new_set.into()));
|
||||
mirror.bsp_set = std::sync::RwLock::new(Some(new_set.into()));
|
||||
}
|
||||
}
|
||||
mirror
|
||||
@@ -563,16 +569,17 @@ impl 缠论K线Py {
|
||||
|
||||
#[getter]
|
||||
fn 买卖点信息(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
if self.bsp_set.borrow().is_none() {
|
||||
if self.bsp_set.read().unwrap().is_none() {
|
||||
let set = pyo3::types::PySet::empty(py)?;
|
||||
for s in self.inner.买卖点信息.borrow().iter() {
|
||||
for s in self.inner.买卖点信息.read().unwrap().iter() {
|
||||
set.add(s.clone())?;
|
||||
}
|
||||
*self.bsp_set.borrow_mut() = Some(set.into());
|
||||
*self.bsp_set.write().unwrap() = Some(set.into());
|
||||
}
|
||||
Ok(self
|
||||
.bsp_set
|
||||
.borrow()
|
||||
.read()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.clone_ref(py)
|
||||
@@ -589,12 +596,13 @@ impl 缠论K线Py {
|
||||
) -> i64 {
|
||||
let rc_list: Vec<_> = 基线
|
||||
.iter()
|
||||
.map(|k| std::rc::Rc::clone(&k.bind(py).borrow().inner))
|
||||
.map(|k| std::sync::Arc::clone(&k.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::kline::chan_kline::缠论K线::时间戳对齐(&rc_list, &k线.borrow().inner)
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
#[pyo3(signature = (时间戳, 高, 低, 方向, 结构, 原始序号, 普k, 之前 = None))]
|
||||
/// 创建新的缠论K线
|
||||
fn 创建缠K(
|
||||
_cls: &Bound<'_, PyType>,
|
||||
@@ -606,7 +614,8 @@ impl 缠论K线Py {
|
||||
原始序号: i64,
|
||||
普k: &Bound<'_, K线Py>,
|
||||
之前: Option<&Bound<'_, Self>>,
|
||||
) -> Self {
|
||||
py: Python<'_>,
|
||||
) -> Py<Self> {
|
||||
let prev_ref = 之前.map(|prev| prev.borrow());
|
||||
let prev_inner = prev_ref.as_ref().map(|r| r.inner.as_ref());
|
||||
let inner = chanlun::kline::chan_kline::缠论K线::创建缠K(
|
||||
@@ -619,7 +628,7 @@ impl 缠论K线Py {
|
||||
普k.borrow().inner.clone(),
|
||||
prev_inner,
|
||||
);
|
||||
Self::from_rc(std::rc::Rc::new(inner))
|
||||
chan_kline_to_py(py, std::sync::Arc::new(inner))
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
@@ -631,7 +640,7 @@ impl 缠论K线Py {
|
||||
当前普K: &Bound<'_, K线Py>,
|
||||
配置: &Bound<'_, 缠论配置Py>,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<(Option<Self>, Option<String>)> {
|
||||
) -> PyResult<(Option<Py<Self>>, Option<String>)> {
|
||||
let mut ck_inner = (*当前缠K.borrow().inner).clone();
|
||||
let config = 配置.borrow().to_rust_config(py)?;
|
||||
let prev_ref = 之前缠K.map(|prev| prev.borrow());
|
||||
@@ -642,7 +651,7 @@ impl 缠论K线Py {
|
||||
&当前普K.borrow().inner,
|
||||
&config,
|
||||
);
|
||||
Ok((result.map(Self::from_rc), mode))
|
||||
Ok((result.map(|rc| chan_kline_to_py(py, rc)), mode))
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
@@ -660,7 +669,7 @@ impl 缠论K线Py {
|
||||
|
||||
let mut ck_seq: Vec<_> = 缠K序列
|
||||
.iter()
|
||||
.map(|k| std::rc::Rc::clone(&k.bind(py).borrow().inner))
|
||||
.map(|k| std::sync::Arc::clone(&k.bind(py).borrow().inner))
|
||||
.collect();
|
||||
let mut bar_seq: Vec<_> = 普K序列
|
||||
.iter()
|
||||
@@ -685,21 +694,22 @@ impl 缠论K线Py {
|
||||
终: &Bound<'_, Self>,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<Vec<Py<Self>>> {
|
||||
let start_ptr = Rc::as_ptr(&始.borrow().inner);
|
||||
let end_ptr = Rc::as_ptr(&终.borrow().inner);
|
||||
let start_ts = 始.borrow().inner.时间戳;
|
||||
let end_ts = 终.borrow().inner.时间戳;
|
||||
let start_ptr = Arc::as_ptr(&始.borrow().inner);
|
||||
let end_ptr = Arc::as_ptr(&终.borrow().inner);
|
||||
let start_ts = 始.borrow().inner.时间戳.load(Ordering::Relaxed);
|
||||
let end_ts = 终.borrow().inner.时间戳.load(Ordering::Relaxed);
|
||||
let start_idx = 序列
|
||||
.iter()
|
||||
.position(|k| {
|
||||
Rc::as_ptr(&k.borrow(py).inner) == start_ptr
|
||||
|| k.borrow(py).inner.时间戳 == start_ts
|
||||
Arc::as_ptr(&k.borrow(py).inner) == start_ptr
|
||||
|| k.borrow(py).inner.时间戳.load(Ordering::Relaxed) == start_ts
|
||||
})
|
||||
.ok_or_else(|| pyo3::exceptions::PyValueError::new_err("始 不在序列中"))?;
|
||||
let end_idx = 序列
|
||||
.iter()
|
||||
.position(|k| {
|
||||
Rc::as_ptr(&k.borrow(py).inner) == end_ptr || k.borrow(py).inner.时间戳 == end_ts
|
||||
Arc::as_ptr(&k.borrow(py).inner) == end_ptr
|
||||
|| k.borrow(py).inner.时间戳.load(Ordering::Relaxed) == end_ts
|
||||
})
|
||||
.ok_or_else(|| pyo3::exceptions::PyValueError::new_err("终 不在序列中"))?;
|
||||
if start_idx > end_idx {
|
||||
|
||||
+233
-185
@@ -25,11 +25,103 @@
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyDict, PyType};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use crate::algorithm_py::中枢Py;
|
||||
use crate::algorithm_py::{hub_to_py, 中枢Py};
|
||||
use crate::config_py::缠论配置Py;
|
||||
use crate::kline_py::{缠论K线Py, K线Py};
|
||||
|
||||
// ---- 身份缓存 (弱引用:通过 refcnt 检测存活,仅缓存持有则视为过期) ----
|
||||
|
||||
thread_local! {
|
||||
static FRACTAL_IDENTITY: RwLock<HashMap<usize, Py<分型Py>>> = RwLock::new(HashMap::new());
|
||||
static DASHED_IDENTITY: RwLock<HashMap<usize, Py<虚线Py>>> = RwLock::new(HashMap::new());
|
||||
static SEGFEAT_IDENTITY: RwLock<HashMap<usize, Py<线段特征Py>>> = RwLock::new(HashMap::new());
|
||||
static FEATFRAC_IDENTITY: RwLock<HashMap<usize, Py<特征分型Py>>> = RwLock::new(HashMap::new());
|
||||
}
|
||||
|
||||
pub(crate) fn fractal_to_py(
|
||||
py: Python<'_>,
|
||||
inner: Arc<chanlun::structure::fractal_obj::分型>,
|
||||
) -> Py<分型Py> {
|
||||
let key = Arc::as_ptr(&inner) as usize;
|
||||
if let Some(cached) =
|
||||
FRACTAL_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
// 清理 refcnt==1 的过期条目(仅缓存持有,Python 侧已无引用)
|
||||
FRACTAL_IDENTITY.with(|c| {
|
||||
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
|
||||
});
|
||||
let obj = Py::new(py, 分型Py { inner }).unwrap();
|
||||
FRACTAL_IDENTITY.with(|c| {
|
||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
||||
});
|
||||
obj
|
||||
}
|
||||
|
||||
pub(crate) fn dashed_to_py(
|
||||
py: Python<'_>,
|
||||
inner: Arc<chanlun::structure::dash_line::虚线>,
|
||||
) -> Py<虚线Py> {
|
||||
let key = Arc::as_ptr(&inner) as usize;
|
||||
if let Some(cached) =
|
||||
DASHED_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
DASHED_IDENTITY.with(|c| {
|
||||
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
|
||||
});
|
||||
let obj = Py::new(py, 虚线Py { inner }).unwrap();
|
||||
DASHED_IDENTITY.with(|c| {
|
||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
||||
});
|
||||
obj
|
||||
}
|
||||
|
||||
pub(crate) fn segfeat_to_py(
|
||||
py: Python<'_>,
|
||||
inner: Arc<chanlun::structure::segment_feat::线段特征>,
|
||||
) -> Py<线段特征Py> {
|
||||
let key = Arc::as_ptr(&inner) as usize;
|
||||
if let Some(cached) =
|
||||
SEGFEAT_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
SEGFEAT_IDENTITY.with(|c| {
|
||||
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
|
||||
});
|
||||
let obj = Py::new(py, 线段特征Py { inner }).unwrap();
|
||||
SEGFEAT_IDENTITY.with(|c| {
|
||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
||||
});
|
||||
obj
|
||||
}
|
||||
|
||||
pub(crate) fn featfrac_to_py(
|
||||
py: Python<'_>,
|
||||
inner: Arc<chanlun::structure::feat_fractal::特征分型>,
|
||||
) -> Py<特征分型Py> {
|
||||
let key = Arc::as_ptr(&inner) as usize;
|
||||
if let Some(cached) =
|
||||
FEATFRAC_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
FEATFRAC_IDENTITY.with(|c| {
|
||||
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
|
||||
});
|
||||
let obj = Py::new(py, 特征分型Py { inner }).unwrap();
|
||||
FEATFRAC_IDENTITY.with(|c| {
|
||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
||||
});
|
||||
obj
|
||||
}
|
||||
use crate::types_py::{分型结构Py, 相对方向Py, 缺口Py};
|
||||
|
||||
// ========== 分型 ==========
|
||||
@@ -47,10 +139,10 @@ use crate::types_py::{分型结构Py, 相对方向Py, 缺口Py};
|
||||
/// 判断分型(缠K序列, 索引, 配置) -> 分型|None — 在缠K序列中指定位置尝试创建分型
|
||||
/// 从缠K序列中获取分型(缠K序列, 配置) -> list[分型] — 扫描全序列提取所有分型
|
||||
/// 向序列中添加(分型序列, 新分型) — 维护分型序列的顺序一致性
|
||||
#[pyclass(name = "分型", module = "chanlun._chanlun", unsendable, from_py_object)]
|
||||
#[pyclass(name = "分型", module = "chanlun._chanlun", from_py_object)]
|
||||
#[derive(Clone)]
|
||||
pub struct 分型Py {
|
||||
pub(crate) inner: Rc<chanlun::structure::fractal_obj::分型>,
|
||||
pub(crate) inner: Arc<chanlun::structure::fractal_obj::分型>,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
@@ -62,33 +154,33 @@ impl 分型Py {
|
||||
右: Option<&Bound<'_, 缠论K线Py>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: Rc::new(chanlun::structure::fractal_obj::分型::new(
|
||||
左.map(|k| Rc::clone(&k.borrow().inner)),
|
||||
Rc::clone(&中.borrow().inner),
|
||||
右.map(|k| Rc::clone(&k.borrow().inner)),
|
||||
inner: Arc::new(chanlun::structure::fractal_obj::分型::new(
|
||||
左.map(|k| Arc::clone(&k.borrow().inner)),
|
||||
Arc::clone(&中.borrow().inner),
|
||||
右.map(|k| Arc::clone(&k.borrow().inner)),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 左(&self) -> Option<缠论K线Py> {
|
||||
fn 左(&self, py: Python<'_>) -> Option<Py<缠论K线Py>> {
|
||||
self.inner
|
||||
.左
|
||||
.as_ref()
|
||||
.map(|k| 缠论K线Py::from_rc(Rc::clone(k)))
|
||||
.map(|k| crate::kline_py::chan_kline_to_py(py, Arc::clone(k)))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 中(&self) -> 缠论K线Py {
|
||||
缠论K线Py::from_rc(Rc::clone(&self.inner.中))
|
||||
fn 中(&self, py: Python<'_>) -> Py<缠论K线Py> {
|
||||
crate::kline_py::chan_kline_to_py(py, Arc::clone(&self.inner.中))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 右(&self) -> Option<缠论K线Py> {
|
||||
fn 右(&self, py: Python<'_>) -> Option<Py<缠论K线Py>> {
|
||||
self.inner
|
||||
.右
|
||||
.as_ref()
|
||||
.map(|k| 缠论K线Py::from_rc(Rc::clone(k)))
|
||||
.map(|k| crate::kline_py::chan_kline_to_py(py, Arc::clone(k)))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -118,13 +210,13 @@ impl 分型Py {
|
||||
|
||||
fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
|
||||
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
|
||||
return Rc::as_ptr(&self.inner) == Rc::as_ptr(&other.inner);
|
||||
return Arc::as_ptr(&self.inner) == Arc::as_ptr(&other.inner);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn __hash__(&self) -> u64 {
|
||||
Rc::as_ptr(&self.inner) as u64
|
||||
Arc::as_ptr(&self.inner) as u64
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -160,11 +252,11 @@ impl 分型Py {
|
||||
dict.set_item("分型特征值", self.分型特征值())?;
|
||||
dict.set_item("强度", self.强度())?;
|
||||
dict.set_item("与MACD柱子分型匹配", self.与MACD柱子分型匹配())?;
|
||||
if let Some(v) = self.左() {
|
||||
if let Some(v) = self.左(py) {
|
||||
dict.set_item("左", v)?;
|
||||
}
|
||||
dict.set_item("中", self.中())?;
|
||||
if let Some(v) = self.右() {
|
||||
dict.set_item("中", self.中(py))?;
|
||||
if let Some(v) = self.右(py) {
|
||||
dict.set_item("右", v)?;
|
||||
}
|
||||
if let Some(v) = self.关系组() {
|
||||
@@ -196,16 +288,16 @@ impl 分型Py {
|
||||
中: &Bound<'_, 缠论K线Py>,
|
||||
py: Python<'_>,
|
||||
) -> Option<Self> {
|
||||
let ck_seq: Vec<Rc<chanlun::kline::chan_kline::缠论K线>> = K线序列
|
||||
let ck_seq: Vec<Arc<chanlun::kline::chan_kline::缠论K线>> = K线序列
|
||||
.iter()
|
||||
.map(|k| Rc::clone(&k.bind(py).borrow().inner))
|
||||
.map(|k| Arc::clone(&k.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::structure::fractal_obj::分型::从缠K序列中获取分型(
|
||||
&ck_seq,
|
||||
&中.borrow().inner,
|
||||
)
|
||||
.map(|inner| Self {
|
||||
inner: Rc::new(inner),
|
||||
inner: Arc::new(inner),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -215,13 +307,7 @@ impl 分型Py {
|
||||
分型序列: &Bound<'_, PyAny>, 当前分型: &Bound<'_, Self>
|
||||
) -> PyResult<()> {
|
||||
let py = 分型序列.py();
|
||||
let inner = Rc::clone(&当前分型.borrow().inner);
|
||||
let wrapper = Py::new(
|
||||
py,
|
||||
Self {
|
||||
inner: Rc::clone(&inner),
|
||||
},
|
||||
)?;
|
||||
let wrapper = fractal_to_py(py, Arc::clone(&当前分型.borrow().inner));
|
||||
分型序列.call_method1("append", (wrapper,))?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -257,10 +343,10 @@ impl 分型Py {
|
||||
/// 计算MACD柱子均值 / 武之全量MACD均值 / 武之MACD均值 / 武之MACD极值
|
||||
/// 计算K线序列MACD趋向背驰 / 买卖意义 / 计算MACD柱子分段
|
||||
/// 密集区域按间隔 / 统计MACD行为
|
||||
#[pyclass(name = "虚线", module = "chanlun._chanlun", unsendable, from_py_object)]
|
||||
#[pyclass(name = "虚线", module = "chanlun._chanlun", from_py_object)]
|
||||
#[derive(Clone)]
|
||||
pub struct 虚线Py {
|
||||
pub(crate) inner: Rc<chanlun::structure::dash_line::虚线>,
|
||||
pub(crate) inner: Arc<chanlun::structure::dash_line::虚线>,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
@@ -276,11 +362,11 @@ impl 虚线Py {
|
||||
有效性: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: Rc::new(chanlun::structure::dash_line::虚线::new(
|
||||
inner: Arc::new(chanlun::structure::dash_line::虚线::new(
|
||||
序号,
|
||||
标识,
|
||||
Rc::clone(&文.borrow().inner),
|
||||
Rc::clone(&武.borrow().inner),
|
||||
Arc::clone(&文.borrow().inner),
|
||||
Arc::clone(&武.borrow().inner),
|
||||
级别,
|
||||
有效性,
|
||||
)),
|
||||
@@ -291,59 +377,50 @@ impl 虚线Py {
|
||||
|
||||
#[getter]
|
||||
fn 标识(&self) -> String {
|
||||
self.inner.标识.clone()
|
||||
self.inner.标识.read().unwrap().clone()
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 序号(&self) -> i64 {
|
||||
self.inner.序号
|
||||
self.inner.序号.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 级别(&self) -> i64 {
|
||||
self.inner.级别
|
||||
self.inner.级别.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 文(&self) -> 分型Py {
|
||||
分型Py {
|
||||
inner: Rc::clone(&self.inner.文),
|
||||
}
|
||||
fn 文(&self, py: Python<'_>) -> Py<分型Py> {
|
||||
fractal_to_py(py, Arc::clone(&self.inner.文))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 武(&self) -> 分型Py {
|
||||
分型Py {
|
||||
inner: Rc::clone(&self.inner.武),
|
||||
}
|
||||
fn 武(&self, py: Python<'_>) -> Py<分型Py> {
|
||||
fractal_to_py(py, Arc::clone(&*self.inner.武.read().unwrap()))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 有效性(&self) -> bool {
|
||||
self.inner.有效性
|
||||
self.inner.有效性.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 模式(&self) -> String {
|
||||
self.inner.模式.clone()
|
||||
self.inner.模式.read().unwrap().clone()
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn _特征序列_显示(&self) -> bool {
|
||||
self.inner._特征序列_显示
|
||||
self.inner._特征序列_显示.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 特征序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for item in &self.inner.特征序列 {
|
||||
for item in self.inner.特征序列.read().unwrap().iter() {
|
||||
match item {
|
||||
Some(feat) => list.append(Py::new(
|
||||
py,
|
||||
线段特征Py {
|
||||
inner: Rc::clone(feat),
|
||||
},
|
||||
)?)?,
|
||||
Some(feat) => list.append(segfeat_to_py(py, Arc::clone(feat)))?,
|
||||
None => {
|
||||
list.append(py.None())?;
|
||||
}
|
||||
@@ -354,27 +431,38 @@ impl 虚线Py {
|
||||
|
||||
#[getter]
|
||||
fn 短路修正(&self) -> bool {
|
||||
self.inner.短路修正
|
||||
self.inner.短路修正.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 确认K线(&self) -> Option<缠论K线Py> {
|
||||
fn 确认K线(&self, py: Python<'_>) -> Option<Py<缠论K线Py>> {
|
||||
self.inner
|
||||
.确认K线
|
||||
.read()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.map(|k| 缠论K线Py::from_rc(Rc::clone(k)))
|
||||
.map(|k| crate::kline_py::chan_kline_to_py(py, Arc::clone(k)))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 前一缺口(&self) -> Option<缺口Py> {
|
||||
self.inner.前一缺口.map(|q| 缺口Py { inner: q })
|
||||
self.inner
|
||||
.前一缺口
|
||||
.read()
|
||||
.unwrap()
|
||||
.map(|q| 缺口Py { inner: q })
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 前一结束位置(&self) -> Option<Self> {
|
||||
self.inner.前一结束位置.as_ref().map(|d| Self {
|
||||
inner: Rc::clone(d),
|
||||
})
|
||||
self.inner
|
||||
.前一结束位置
|
||||
.read()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.map(|d| Self {
|
||||
inner: Arc::clone(d),
|
||||
})
|
||||
}
|
||||
|
||||
// ---- 序列 getters ----
|
||||
@@ -382,11 +470,11 @@ impl 虚线Py {
|
||||
#[getter]
|
||||
fn 基础序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for d in &self.inner.基础序列 {
|
||||
for d in self.inner.基础序列.read().unwrap().iter() {
|
||||
list.append(Py::new(
|
||||
py,
|
||||
Self {
|
||||
inner: Rc::clone(d),
|
||||
inner: Arc::clone(d),
|
||||
},
|
||||
)?)?;
|
||||
}
|
||||
@@ -396,10 +484,8 @@ impl 虚线Py {
|
||||
#[getter]
|
||||
fn 实_中枢序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for h in &self.inner.实_中枢序列 {
|
||||
list.append(中枢Py {
|
||||
inner: Rc::clone(h),
|
||||
})?;
|
||||
for h in self.inner.实_中枢序列.read().unwrap().iter() {
|
||||
list.append(hub_to_py(py, Arc::clone(h)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -407,10 +493,8 @@ impl 虚线Py {
|
||||
#[getter]
|
||||
fn 虚_中枢序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for h in &self.inner.虚_中枢序列 {
|
||||
list.append(中枢Py {
|
||||
inner: Rc::clone(h),
|
||||
})?;
|
||||
for h in self.inner.虚_中枢序列.read().unwrap().iter() {
|
||||
list.append(hub_to_py(py, Arc::clone(h)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -418,10 +502,8 @@ impl 虚线Py {
|
||||
#[getter]
|
||||
fn 合_中枢序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for h in &self.inner.合_中枢序列 {
|
||||
list.append(中枢Py {
|
||||
inner: Rc::clone(h),
|
||||
})?;
|
||||
for h in self.inner.合_中枢序列.read().unwrap().iter() {
|
||||
list.append(hub_to_py(py, Arc::clone(h)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -432,11 +514,11 @@ impl 虚线Py {
|
||||
/// 笔序列
|
||||
fn 笔序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for d in self.inner.笔序列() {
|
||||
for d in self.inner.基础序列.read().unwrap().iter() {
|
||||
list.append(Py::new(
|
||||
py,
|
||||
Self {
|
||||
inner: Rc::clone(d),
|
||||
inner: Arc::clone(d),
|
||||
},
|
||||
)?)?;
|
||||
}
|
||||
@@ -490,7 +572,7 @@ impl 虚线Py {
|
||||
let list = pyo3::types::PyList::empty(观察员.py());
|
||||
for k in &result {
|
||||
list.append(K线Py {
|
||||
inner: Rc::clone(k),
|
||||
inner: Arc::clone(k),
|
||||
})?;
|
||||
}
|
||||
Ok(list.into())
|
||||
@@ -502,21 +584,21 @@ impl 虚线Py {
|
||||
观察员: &Bound<'_, crate::business_py::观察者Py>,
|
||||
) -> PyResult<Py<PyAny>> {
|
||||
let obs_ref = 观察员.borrow();
|
||||
let py = 观察员.py();
|
||||
let observer_inner = obs_ref.obs();
|
||||
let result = self.inner.获取缠K序列(&observer_inner.缠论K线序列);
|
||||
let list = pyo3::types::PyList::empty(观察员.py());
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for k in &result {
|
||||
list.append(缠论K线Py::from_rc(Rc::clone(k)))?;
|
||||
list.append(crate::kline_py::chan_kline_to_py(py, Arc::clone(k)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
/// 递归获取虚线的终点分型(笔直接返回武,线段递归到底层笔的武)
|
||||
fn 获取_武(_cls: &Bound<'_, PyType>, 实线: &Bound<'_, Self>) -> 分型Py {
|
||||
分型Py {
|
||||
inner: 实线.borrow().inner.获取_武(),
|
||||
}
|
||||
fn 获取_武(_cls: &Bound<'_, PyType>, 实线: &Bound<'_, Self>) -> Py<分型Py> {
|
||||
let py = _cls.py();
|
||||
fractal_to_py(py, 实线.borrow().inner.获取_武())
|
||||
}
|
||||
|
||||
/// 获取用于保存的数据文本
|
||||
@@ -535,8 +617,8 @@ impl 虚线Py {
|
||||
dict.set_item("模式", self.模式())?;
|
||||
dict.set_item("短路修正", self.短路修正())?;
|
||||
dict.set_item("图表标题", self.图表标题())?;
|
||||
dict.set_item("文_时间戳", self.文().时间戳())?;
|
||||
dict.set_item("武_时间戳", self.武().时间戳())?;
|
||||
dict.set_item("文_时间戳", self.文(py).bind(py).borrow().时间戳())?;
|
||||
dict.set_item("武_时间戳", self.武(py).bind(py).borrow().时间戳())?;
|
||||
Ok(dict.into())
|
||||
}
|
||||
|
||||
@@ -550,18 +632,19 @@ impl 虚线Py {
|
||||
|
||||
fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
|
||||
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
|
||||
return Rc::as_ptr(&self.inner) == Rc::as_ptr(&other.inner);
|
||||
return Arc::as_ptr(&self.inner) == Arc::as_ptr(&other.inner);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn __hash__(&self) -> u64 {
|
||||
Rc::as_ptr(&self.inner) as u64
|
||||
Arc::as_ptr(&self.inner) as u64
|
||||
}
|
||||
|
||||
// ---- 静态工厂方法 ----
|
||||
|
||||
#[classmethod]
|
||||
#[pyo3(signature = (文, 武, 有效性 = true))]
|
||||
/// :param 文: 起点分型
|
||||
fn 创建笔(
|
||||
_cls: &Bound<'_, PyType>,
|
||||
@@ -570,9 +653,9 @@ impl 虚线Py {
|
||||
有效性: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: Rc::new(chanlun::structure::dash_line::虚线::创建笔(
|
||||
Rc::clone(&文.borrow().inner),
|
||||
Rc::clone(&武.borrow().inner),
|
||||
inner: Arc::new(chanlun::structure::dash_line::虚线::创建笔(
|
||||
Arc::clone(&文.borrow().inner),
|
||||
Arc::clone(&武.borrow().inner),
|
||||
有效性,
|
||||
)),
|
||||
}
|
||||
@@ -581,12 +664,12 @@ impl 虚线Py {
|
||||
#[classmethod]
|
||||
/// :param 虚线序列: 构成线段的虚线列表(笔)
|
||||
fn 创建线段(_cls: &Bound<'_, PyType>, 虚线序列: Vec<Py<Self>>, py: Python<'_>) -> Self {
|
||||
let rc_list: Vec<Rc<chanlun::structure::dash_line::虚线>> = 虚线序列
|
||||
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 虚线序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
Self {
|
||||
inner: Rc::new(chanlun::structure::dash_line::虚线::创建线段(
|
||||
inner: Arc::new(chanlun::structure::dash_line::虚线::创建线段(
|
||||
&rc_list,
|
||||
)),
|
||||
}
|
||||
@@ -654,7 +737,7 @@ impl 虚线Py {
|
||||
实线: &Bound<'_, Self>,
|
||||
py: Python<'_>,
|
||||
) -> f64 {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -672,7 +755,7 @@ impl 虚线Py {
|
||||
实线: &Bound<'_, Self>,
|
||||
py: Python<'_>,
|
||||
) -> bool {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -690,7 +773,7 @@ impl 虚线Py {
|
||||
实线: &Bound<'_, Self>,
|
||||
py: Python<'_>,
|
||||
) -> bool {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -705,7 +788,7 @@ impl 虚线Py {
|
||||
实线: &Bound<'_, Self>,
|
||||
py: Python<'_>,
|
||||
) -> bool {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -720,7 +803,7 @@ impl 虚线Py {
|
||||
实线: &Bound<'_, Self>,
|
||||
py: Python<'_>,
|
||||
) -> Option<f64> {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -738,7 +821,7 @@ impl 虚线Py {
|
||||
实线: &Bound<'_, Self>,
|
||||
py: Python<'_>,
|
||||
) -> Option<f64> {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -756,7 +839,7 @@ impl 虚线Py {
|
||||
实线: &Bound<'_, Self>,
|
||||
py: Python<'_>,
|
||||
) -> bool {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -771,7 +854,7 @@ impl 虚线Py {
|
||||
实线: &Bound<'_, Self>,
|
||||
py: Python<'_>,
|
||||
) -> bool {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -786,7 +869,7 @@ impl 虚线Py {
|
||||
方向: &Bound<'_, 相对方向Py>,
|
||||
py: Python<'_>,
|
||||
) -> [bool; 3] {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -803,7 +886,7 @@ impl 虚线Py {
|
||||
k线序列: Vec<Py<K线Py>>,
|
||||
py: Python<'_>,
|
||||
) -> Vec<Vec<f64>> {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = k线序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = k线序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -811,6 +894,7 @@ impl 虚线Py {
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
#[pyo3(signature = (交叉标记, 最大间隔 = 5usize, 最少交叉数 = 3usize))]
|
||||
/// 交叉标记: 长度为len(macd_list)的列表,0=无交叉, 1=金叉, -1=死叉
|
||||
fn 密集区域按间隔(
|
||||
_cls: &Bound<'_, PyType>,
|
||||
@@ -826,6 +910,7 @@ impl 虚线Py {
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
#[pyo3(signature = (普K序列, 最大间隔 = 8usize, 最少交叉数 = 3usize))]
|
||||
/// :param 普K序列: K线序列
|
||||
fn 统计MACD行为(
|
||||
_cls: &Bound<'_, PyType>,
|
||||
@@ -834,7 +919,7 @@ impl 虚线Py {
|
||||
最少交叉数: usize,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<Py<PyAny>> {
|
||||
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = 普K序列
|
||||
let rc_list: Vec<Arc<chanlun::kline::bar::K线>> = 普K序列
|
||||
.iter()
|
||||
.map(|k| k.bind(py).borrow().inner.clone())
|
||||
.collect();
|
||||
@@ -898,15 +983,10 @@ impl 虚线Py {
|
||||
/// 新建(序号, 文, 武, 基础序列?) -> 线段特征
|
||||
/// 静态分析(虚线序列, 配置) -> 线段特征|None
|
||||
/// 获取分型序列(虚线序列, 配置) -> list[线段特征]
|
||||
#[pyclass(
|
||||
name = "线段特征",
|
||||
module = "chanlun._chanlun",
|
||||
unsendable,
|
||||
from_py_object
|
||||
)]
|
||||
#[pyclass(name = "线段特征", module = "chanlun._chanlun", from_py_object)]
|
||||
#[derive(Clone)]
|
||||
pub struct 线段特征Py {
|
||||
pub(crate) inner: Rc<chanlun::structure::segment_feat::线段特征>,
|
||||
pub(crate) inner: Arc<chanlun::structure::segment_feat::线段特征>,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
@@ -918,12 +998,12 @@ impl 线段特征Py {
|
||||
线段方向: &Bound<'_, 相对方向Py>,
|
||||
py: Python<'_>,
|
||||
) -> Self {
|
||||
let rc_list: Vec<Rc<chanlun::structure::dash_line::虚线>> = 基础序列
|
||||
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 基础序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
Self {
|
||||
inner: Rc::new(chanlun::structure::segment_feat::线段特征::new(
|
||||
inner: Arc::new(chanlun::structure::segment_feat::线段特征::new(
|
||||
标识,
|
||||
rc_list,
|
||||
线段方向.borrow().inner,
|
||||
@@ -954,12 +1034,7 @@ impl 线段特征Py {
|
||||
fn 元素(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for d in &self.inner.元素 {
|
||||
list.append(Py::new(
|
||||
py,
|
||||
虚线Py {
|
||||
inner: Rc::clone(d),
|
||||
},
|
||||
)?)?;
|
||||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -985,38 +1060,27 @@ impl 线段特征Py {
|
||||
)));
|
||||
}
|
||||
let dash = &self.inner.元素[idx as usize];
|
||||
let obj: Py<PyAny> = Py::new(
|
||||
py,
|
||||
虚线Py {
|
||||
inner: Rc::clone(dash),
|
||||
},
|
||||
)?
|
||||
.into();
|
||||
let obj: Py<PyAny> = dashed_to_py(py, Arc::clone(dash)).into();
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
fn __iter__(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for d in &self.inner.元素 {
|
||||
list.append(Py::new(
|
||||
py,
|
||||
虚线Py {
|
||||
inner: Rc::clone(d),
|
||||
},
|
||||
)?)?;
|
||||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||||
}
|
||||
list.call_method0("__iter__").map(|iter| iter.into())
|
||||
}
|
||||
|
||||
fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
|
||||
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
|
||||
return Rc::as_ptr(&self.inner) == Rc::as_ptr(&other.inner);
|
||||
return Arc::as_ptr(&self.inner) == Arc::as_ptr(&other.inner);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn __hash__(&self) -> u64 {
|
||||
Rc::as_ptr(&self.inner) as u64
|
||||
Arc::as_ptr(&self.inner) as u64
|
||||
}
|
||||
|
||||
/// pandas 兼容 — 返回关键标量字段构成的字典
|
||||
@@ -1040,18 +1104,14 @@ impl 线段特征Py {
|
||||
|
||||
#[getter]
|
||||
/// 起点分型(向上线段取高高中的最大者,向下线段取低低中的最小者)
|
||||
fn 文(&self) -> 分型Py {
|
||||
分型Py {
|
||||
inner: self.inner.文(),
|
||||
}
|
||||
fn 文(&self, py: Python<'_>) -> Py<分型Py> {
|
||||
fractal_to_py(py, self.inner.文())
|
||||
}
|
||||
|
||||
#[getter]
|
||||
/// 终点分型(向上线段取高高中的最大者,向下线段取低低中的最小者)
|
||||
fn 武(&self) -> 分型Py {
|
||||
分型Py {
|
||||
inner: self.inner.武(),
|
||||
}
|
||||
fn 武(&self, py: Python<'_>) -> Py<分型Py> {
|
||||
fractal_to_py(py, self.inner.武())
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -1076,17 +1136,17 @@ impl 线段特征Py {
|
||||
|
||||
/// :param 待添加虚线: 待添加的虚线
|
||||
fn 添加(&mut self, 待添加虚线: &Bound<'_, 虚线Py>) -> PyResult<()> {
|
||||
let inner = Rc::make_mut(&mut self.inner);
|
||||
let inner = Arc::make_mut(&mut self.inner);
|
||||
inner
|
||||
.添加(Rc::clone(&待添加虚线.borrow().inner))
|
||||
.添加(Arc::clone(&待添加虚线.borrow().inner))
|
||||
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e))
|
||||
}
|
||||
|
||||
/// :param 待删除虚线: 待删除的虚线
|
||||
fn 删除(&mut self, 待删除虚线: &Bound<'_, 虚线Py>) -> PyResult<()> {
|
||||
let inner = Rc::make_mut(&mut self.inner);
|
||||
let inner = Arc::make_mut(&mut self.inner);
|
||||
inner
|
||||
.删除(&Rc::clone(&待删除虚线.borrow().inner))
|
||||
.删除(&Arc::clone(&待删除虚线.borrow().inner))
|
||||
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e))
|
||||
}
|
||||
|
||||
@@ -1100,12 +1160,12 @@ impl 线段特征Py {
|
||||
线段方向: &Bound<'_, 相对方向Py>,
|
||||
py: Python<'_>,
|
||||
) -> Self {
|
||||
let rc_list: Vec<Rc<chanlun::structure::dash_line::虚线>> = 虚线序列
|
||||
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 虚线序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
Self {
|
||||
inner: Rc::new(chanlun::structure::segment_feat::线段特征::新建(
|
||||
inner: Arc::new(chanlun::structure::segment_feat::线段特征::新建(
|
||||
rc_list,
|
||||
线段方向.borrow().inner,
|
||||
)),
|
||||
@@ -1113,6 +1173,7 @@ impl 线段特征Py {
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
#[pyo3(signature = (虚线序列, 线段方向, 四象, 是否忽视 = false))]
|
||||
/// 静态分析虚线序列,生成特征序列
|
||||
fn 静态分析(
|
||||
_cls: &Bound<'_, PyType>,
|
||||
@@ -1122,9 +1183,9 @@ impl 线段特征Py {
|
||||
是否忽视: bool,
|
||||
py: Python<'_>,
|
||||
) -> Vec<Self> {
|
||||
let rc_list: Vec<Rc<chanlun::structure::dash_line::虚线>> = 虚线序列
|
||||
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 虚线序列
|
||||
.iter()
|
||||
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::structure::segment_feat::线段特征::静态分析(
|
||||
&rc_list,
|
||||
@@ -1143,16 +1204,14 @@ impl 线段特征Py {
|
||||
_cls: &Bound<'_, PyType>,
|
||||
特征序列: Vec<Py<Self>>,
|
||||
py: Python<'_>,
|
||||
) -> Vec<特征分型Py> {
|
||||
let rc_list: Vec<Rc<chanlun::structure::segment_feat::线段特征>> = 特征序列
|
||||
) -> Vec<Py<特征分型Py>> {
|
||||
let rc_list: Vec<Arc<chanlun::structure::segment_feat::线段特征>> = 特征序列
|
||||
.iter()
|
||||
.map(|s| Rc::clone(&s.bind(py).borrow().inner))
|
||||
.map(|s| Arc::clone(&s.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::structure::segment_feat::线段特征::获取分型序列(&rc_list)
|
||||
.into_iter()
|
||||
.map(|inner| 特征分型Py {
|
||||
inner: Rc::new(inner),
|
||||
})
|
||||
.map(|inner| featfrac_to_py(py, Arc::new(inner)))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
@@ -1164,15 +1223,10 @@ impl 线段特征Py {
|
||||
/// 属性 (只读):
|
||||
/// 左: 线段特征|None / 中: 线段特征 / 右: 线段特征|None
|
||||
/// 结构: 分型结构 — 顶/底分型判定结果
|
||||
#[pyclass(
|
||||
name = "特征分型",
|
||||
module = "chanlun._chanlun",
|
||||
unsendable,
|
||||
from_py_object
|
||||
)]
|
||||
#[pyclass(name = "特征分型", module = "chanlun._chanlun", from_py_object)]
|
||||
#[derive(Clone)]
|
||||
pub struct 特征分型Py {
|
||||
pub(crate) inner: Rc<chanlun::structure::feat_fractal::特征分型>,
|
||||
pub(crate) inner: Arc<chanlun::structure::feat_fractal::特征分型>,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
@@ -1185,34 +1239,28 @@ impl 特征分型Py {
|
||||
结构: &Bound<'_, 分型结构Py>,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: Rc::new(chanlun::structure::feat_fractal::特征分型::new(
|
||||
Rc::clone(&左.borrow().inner),
|
||||
Rc::clone(&中.borrow().inner),
|
||||
Rc::clone(&右.borrow().inner),
|
||||
inner: Arc::new(chanlun::structure::feat_fractal::特征分型::new(
|
||||
Arc::clone(&左.borrow().inner),
|
||||
Arc::clone(&中.borrow().inner),
|
||||
Arc::clone(&右.borrow().inner),
|
||||
结构.borrow().inner,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 左(&self) -> 线段特征Py {
|
||||
线段特征Py {
|
||||
inner: Rc::clone(&self.inner.左),
|
||||
}
|
||||
fn 左(&self, py: Python<'_>) -> Py<线段特征Py> {
|
||||
segfeat_to_py(py, Arc::clone(&self.inner.左))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 中(&self) -> 线段特征Py {
|
||||
线段特征Py {
|
||||
inner: Rc::clone(&self.inner.中),
|
||||
}
|
||||
fn 中(&self, py: Python<'_>) -> Py<线段特征Py> {
|
||||
segfeat_to_py(py, Arc::clone(&self.inner.中))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 右(&self) -> 线段特征Py {
|
||||
线段特征Py {
|
||||
inner: Rc::clone(&self.inner.右),
|
||||
}
|
||||
fn 右(&self, py: Python<'_>) -> Py<线段特征Py> {
|
||||
segfeat_to_py(py, Arc::clone(&self.inner.右))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -1240,13 +1288,13 @@ impl 特征分型Py {
|
||||
|
||||
fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
|
||||
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
|
||||
return Rc::as_ptr(&self.inner) == Rc::as_ptr(&other.inner);
|
||||
return Arc::as_ptr(&self.inner) == Arc::as_ptr(&other.inner);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn __hash__(&self) -> u64 {
|
||||
Rc::as_ptr(&self.inner) as u64
|
||||
Arc::as_ptr(&self.inner) as u64
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -286,17 +286,15 @@ impl 分型结构Py {
|
||||
/// 返回:
|
||||
/// 分型结构 或 None — 无法判定时返回 None
|
||||
#[classmethod]
|
||||
#[pyo3(signature = (左, 中, 右, 可以逆序包含 = false, 忽视顺序包含 = false))]
|
||||
fn 分析(
|
||||
_cls: &Bound<'_, PyType>,
|
||||
左: &Bound<'_, PyAny>,
|
||||
中: &Bound<'_, PyAny>,
|
||||
右: &Bound<'_, PyAny>,
|
||||
可以逆序包含: Option<bool>,
|
||||
忽视顺序包含: Option<bool>,
|
||||
可以逆序包含: bool,
|
||||
忽视顺序包含: bool,
|
||||
) -> PyResult<Option<Self>> {
|
||||
let 可以逆序包含 = 可以逆序包含.unwrap_or(false);
|
||||
let 忽视顺序包含 = 忽视顺序包含.unwrap_or(false);
|
||||
|
||||
let get_hl = |obj: &Bound<'_, PyAny>| -> PyResult<(f64, f64)> {
|
||||
Ok((
|
||||
obj.getattr("高")?.extract::<f64>()?,
|
||||
@@ -456,13 +454,13 @@ impl 缺口Py {
|
||||
/// 返回:
|
||||
/// 缺口 或 None — 起点==终点时返回 None
|
||||
#[classmethod]
|
||||
#[pyo3(signature = (起点, 终点, 比例 = 0.15))]
|
||||
fn 居中截取区间(
|
||||
_cls: &Bound<'_, PyType>,
|
||||
起点: f64,
|
||||
终点: f64,
|
||||
比例: Option<f64>,
|
||||
比例: f64,
|
||||
) -> Option<Self> {
|
||||
let 比例 = 比例.unwrap_or(0.15);
|
||||
chanlun::types::缺口::居中截取区间(起点, 终点, 比例).map(|inner| Self { inner })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
对象标识测试:验证同一 Rc 底层数据通过不同路径访问时,
|
||||
Python 侧始终返回相同的 PyObject(`is` 比较为 True)。
|
||||
|
||||
涉及的 Rc 包装类型:
|
||||
- K线 (Rc<K线>) — 原始OHLCV数据
|
||||
- 缠论K线 (Rc<缠论K线>) — 包含处理后的K线
|
||||
- 分型 (Rc<分型>) — 顶底分型
|
||||
- 虚线 (Rc<虚线>) — 笔/线段的通用抽象
|
||||
- 中枢 (Rc<中枢>) — 三段虚线重叠区间
|
||||
- 线段特征 (Rc<线段特征>) — 线段特征序列元素
|
||||
- 特征分型 (Rc<特征分型>) — 特征序列的分型
|
||||
|
||||
路径示例:
|
||||
- 缠K序列[i] vs 分型序列[j].中 (同一根缠K)
|
||||
- 分型序列[i] vs 笔序列[j].文 (同一个分型)
|
||||
- 笔序列[i] vs 中枢[k].基础序列[m] (同一条虚线)
|
||||
"""
|
||||
|
||||
import chanlun
|
||||
import math
|
||||
|
||||
|
||||
def create_observer(symbol="btcusd", period=14400, n_bars=500):
|
||||
"""创建观察者并喂入模拟K线数据。"""
|
||||
cfg = chanlun.缠论配置()
|
||||
obs = chanlun.观察者(symbol, period, cfg)
|
||||
|
||||
for i in range(n_bars):
|
||||
trend = i * 3
|
||||
wave = math.sin(i * 0.05) * 2000
|
||||
mid = 68000.0 + trend + wave
|
||||
high = mid + abs(math.cos(i * 0.3)) * 400 + 100
|
||||
low = mid - abs(math.sin(i * 0.5)) * 400 - 100
|
||||
k = chanlun.K线(
|
||||
标识=symbol,
|
||||
周期=period,
|
||||
时间戳=1771675200 + i * period,
|
||||
开盘价=mid - 50,
|
||||
高=high,
|
||||
低=low,
|
||||
收盘价=mid + 50,
|
||||
成交量=abs(math.sin(i)) * 1000,
|
||||
)
|
||||
obs.增加原始K线(k)
|
||||
|
||||
return obs
|
||||
|
||||
|
||||
class Test缠K身份:
|
||||
"""缠论K线: 从序列、分型、笔端点、中枢等不同路径访问。"""
|
||||
|
||||
def test_序列重复获取(self):
|
||||
"""同一序列获取两次,元素应相同。"""
|
||||
obs = create_observer()
|
||||
seq1 = obs.缠论K线序列
|
||||
seq2 = obs.缠论K线序列
|
||||
for i in range(min(len(seq1), 10)):
|
||||
assert seq1[i] is seq2[i], f"缠K序列[{i}] 身份不一致"
|
||||
|
||||
def test_分型中K(self):
|
||||
"""分型.中 与 缠K序列 对应元素应相同。"""
|
||||
obs = create_observer()
|
||||
seq = obs.缠论K线序列
|
||||
分序 = obs.分型序列
|
||||
for fx in 分序[:10]:
|
||||
中 = fx.中
|
||||
for ck in seq:
|
||||
if ck.时间戳 == 中.时间戳:
|
||||
assert ck is 中, f"分型.中 (ts={中.时间戳}) 与序列中元素不匹配"
|
||||
break
|
||||
|
||||
def test_笔端点钟K(self):
|
||||
"""笔的端点分型的中间K线应与序列元素相同。"""
|
||||
obs = create_observer()
|
||||
seq = obs.缠论K线序列
|
||||
for bi in obs.笔序列:
|
||||
for nm, getter in [("文", lambda b=bi: b.文), ("武", lambda b=bi: b.武)]:
|
||||
ep = getter()
|
||||
if ep is None:
|
||||
continue
|
||||
中 = ep.中
|
||||
for ck in seq:
|
||||
if ck.时间戳 == 中.时间戳:
|
||||
assert ck is 中, f"笔.{nm}.中 (ts={中.时间戳}) 与序列中元素不匹配"
|
||||
break
|
||||
|
||||
def test_getter重复调用(self):
|
||||
"""同一getter调用两次返回同一对象。"""
|
||||
obs = create_observer()
|
||||
for fx in obs.分型序列[:5]:
|
||||
中1 = fx.中
|
||||
中2 = fx.中
|
||||
assert 中1 is 中2, "分型.中 两次调用返回不同对象"
|
||||
|
||||
|
||||
class Test分型身份:
|
||||
"""分型: 从分型序列、笔/线段端点、买卖点等不同路径访问。"""
|
||||
|
||||
def test_序列重复获取(self):
|
||||
"""同一序列获取两次,元素应相同。"""
|
||||
obs = create_observer()
|
||||
seq1 = obs.分型序列
|
||||
seq2 = obs.分型序列
|
||||
for i in range(min(len(seq1), 9)):
|
||||
assert seq1[i] is seq2[i], f"分型序列[{i}] 身份不一致"
|
||||
|
||||
def test_笔端点与序列(self):
|
||||
"""笔.文 / 笔.武 应与分型序列中对应元素相同。"""
|
||||
obs = create_observer()
|
||||
分序 = obs.分型序列
|
||||
for bi in obs.笔序列:
|
||||
for nm in ["文", "武"]:
|
||||
ep = getattr(bi, nm)
|
||||
if ep is None:
|
||||
continue
|
||||
matched = False
|
||||
for fx in 分序:
|
||||
if fx.时间戳 == ep.时间戳 and fx.结构 == ep.结构:
|
||||
assert fx is ep, f"笔.{nm} (ts={ep.时间戳}) 与分型序列中元素不匹配"
|
||||
matched = True
|
||||
break
|
||||
assert matched, f"笔.{nm} (ts={ep.时间戳}) 在分型序列中未找到"
|
||||
|
||||
def test_段端点与序列(self):
|
||||
"""段.文 / 段.武 应与分型序列中对应元素相同。"""
|
||||
obs = create_observer()
|
||||
分序 = obs.分型序列
|
||||
for duan in obs.线段序列:
|
||||
for nm in ["文", "武"]:
|
||||
ep = getattr(duan, nm)
|
||||
if ep is None:
|
||||
continue
|
||||
matched = False
|
||||
for fx in 分序:
|
||||
if fx.时间戳 == ep.时间戳 and fx.结构 == ep.结构:
|
||||
assert fx is ep, f"段.{nm} (ts={ep.时间戳}) 与分型序列中元素不匹配"
|
||||
matched = True
|
||||
break
|
||||
assert matched, f"段.{nm} (ts={ep.时间戳}) 在分型序列中未找到"
|
||||
|
||||
def test_getter重复调用(self):
|
||||
"""同一getter调用两次返回同一对象。"""
|
||||
obs = create_observer()
|
||||
for bi in obs.笔序列:
|
||||
文1 = bi.文
|
||||
文2 = bi.文
|
||||
assert 文1 is 文2, "笔.文 两次调用返回不同对象"
|
||||
武1 = bi.武
|
||||
武2 = bi.武
|
||||
assert 武1 is 武2, "笔.武 两次调用返回不同对象"
|
||||
break # 只测第一笔
|
||||
|
||||
|
||||
class Test虚线身份:
|
||||
"""虚线(笔/线段): 从笔序列、线段序列、中枢内部序列等不同路径访问。"""
|
||||
|
||||
def test_笔序列重复获取(self):
|
||||
obs = create_observer()
|
||||
seq1 = obs.笔序列
|
||||
seq2 = obs.笔序列
|
||||
for i in range(min(len(seq1), 8)):
|
||||
assert seq1[i] is seq2[i], f"笔序列[{i}] 身份不一致"
|
||||
|
||||
def test_线段序列重复获取(self):
|
||||
obs = create_observer()
|
||||
seq1 = obs.线段序列
|
||||
seq2 = obs.线段序列
|
||||
for i in range(min(len(seq1), 5)):
|
||||
assert seq1[i] is seq2[i], f"线段序列[{i}] 身份不一致"
|
||||
|
||||
def test_多个扩展序列(self):
|
||||
"""扩展线段的不同序列获取同一虚线应相同。"""
|
||||
obs = create_observer()
|
||||
s1 = obs.扩展线段序列
|
||||
s2 = obs.扩展线段序列_线段
|
||||
s3 = obs.扩展线段序列_扩展线段
|
||||
# 这些序列可能包含不同的虚线,但如果同一个 Rc 出现在两个序列中应该相同
|
||||
for d1 in s1:
|
||||
for d2 in s2:
|
||||
if d1.序号 == d2.序号:
|
||||
assert d1 is d2, f"扩展线段序列[{d1.序号}] 跨序列身份不一致"
|
||||
break
|
||||
|
||||
|
||||
class TestK线身份:
|
||||
"""原始K线: 从序列、买卖点、缠K标的等不同路径访问。"""
|
||||
|
||||
def test_序列重复获取(self):
|
||||
obs = create_observer()
|
||||
seq1 = obs.普通K线序列
|
||||
seq2 = obs.普通K线序列
|
||||
for i in range(min(len(seq1), 10)):
|
||||
assert seq1[i] is seq2[i], f"普K序列[{i}] 身份不一致"
|
||||
|
||||
|
||||
class Test中枢身份:
|
||||
"""中枢: 从中枢序列、分型关联、笔中枢/线段中枢等不同路径访问。"""
|
||||
|
||||
def test_序列重复获取(self):
|
||||
obs = create_observer(period=3600, n_bars=800)
|
||||
seq1 = obs.中枢序列
|
||||
seq2 = obs.中枢序列
|
||||
for i in range(min(len(seq1), 5)):
|
||||
assert seq1[i] is seq2[i], f"中枢序列[{i}] 身份不一致"
|
||||
|
||||
def test_笔中枢与线段中枢(self):
|
||||
obs = create_observer(period=3600, n_bars=800)
|
||||
笔中 = obs.笔_中枢序列
|
||||
段中 = obs.线段_中枢序列
|
||||
扩展中 = obs.扩展中枢序列
|
||||
# 验证同一次获取内的身份
|
||||
for zs in 笔中:
|
||||
文1 = zs.文
|
||||
文2 = zs.文
|
||||
assert 文1 is 文2, f"笔中枢.文 两次调用不同"
|
||||
break
|
||||
for zs in 段中:
|
||||
文1 = zs.文
|
||||
文2 = zs.文
|
||||
assert 文1 is 文2, f"段中枢.文 两次调用不同"
|
||||
break
|
||||
|
||||
|
||||
class Test整体身份:
|
||||
"""跨类型综合身份测试。"""
|
||||
|
||||
def test_买卖点分型(self):
|
||||
"""验证买卖点的关联分型身份。"""
|
||||
obs = create_observer(period=3600, n_bars=800)
|
||||
# 尝试访问可用的结构
|
||||
分序 = obs.分型序列
|
||||
笔序 = obs.笔序列
|
||||
assert len(分序) >= 0 and len(笔序) >= 0 # 至少不崩溃
|
||||
|
||||
def test_全链路一致性(self):
|
||||
"""缠K → 分型 → 笔 → 段 链路中所有对象身份一致。"""
|
||||
obs = create_observer()
|
||||
seq = obs.缠论K线序列
|
||||
|
||||
for bi in obs.笔序列:
|
||||
# 笔的端点分型
|
||||
for nm, getter in [("文", lambda b=bi: b.文), ("武", lambda b=bi: b.武)]:
|
||||
ep = getter()
|
||||
if ep is None:
|
||||
continue
|
||||
# ep 中的 中 是一根缠K,应能在序列中找到相同对象
|
||||
中 = ep.中
|
||||
for ck in seq:
|
||||
if ck.时间戳 == 中.时间戳:
|
||||
assert ck is 中
|
||||
break
|
||||
# 左也应该是可访问的
|
||||
左 = ep.左
|
||||
if 左 is not None:
|
||||
for ck in seq:
|
||||
if ck.时间戳 == 左.时间戳:
|
||||
assert ck is 左
|
||||
break
|
||||
# 右也应该是可访问的
|
||||
右 = ep.右
|
||||
if 右 is not None:
|
||||
for ck in seq:
|
||||
if ck.时间戳 == 右.时间戳:
|
||||
assert ck is 右
|
||||
break
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "chanlun"
|
||||
version = "26.5.2"
|
||||
version = "26.5.3"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
license = "MIT"
|
||||
|
||||
+223
-159
@@ -29,7 +29,8 @@ use crate::kline::chan_kline::缠论K线;
|
||||
use crate::structure::dash_line::虚线;
|
||||
use crate::structure::fractal_obj::分型;
|
||||
use crate::types::{分型结构, 相对方向};
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// 笔 — 从分型生成笔的算法集合(静态方法命名空间)
|
||||
pub struct 笔;
|
||||
@@ -37,8 +38,8 @@ pub struct 笔;
|
||||
impl 笔 {
|
||||
/// 获取可成笔的缠K数量(考虑弱化模式)
|
||||
pub fn 获取缠K数量(
|
||||
缠K序列: &[Rc<缠论K线>],
|
||||
笔序列: &[Rc<虚线>],
|
||||
缠K序列: &[Arc<缠论K线>],
|
||||
笔序列: &[Arc<虚线>],
|
||||
配置: &缠论配置,
|
||||
) -> usize {
|
||||
let 实际数量 = 缠K序列.len();
|
||||
@@ -51,7 +52,9 @@ impl 笔 {
|
||||
let 实际低点 = Self::实际低点(缠K序列, 配置.笔内相同终点取舍);
|
||||
|
||||
if let (Some(ref 高点), Some(ref 低点)) = (&实际高点, &实际低点) {
|
||||
let 原始数量 = 1 + (低点.标的K线.序号 - 高点.标的K线.序号).unsigned_abs() as usize;
|
||||
let 原始数量 = 1
|
||||
+ (低点.标的K线.read().unwrap().序号 - 高点.标的K线.read().unwrap().序号)
|
||||
.unsigned_abs() as usize;
|
||||
if 原始数量 >= 配置.笔内元素数量 as usize {
|
||||
return 配置.笔内元素数量 as usize;
|
||||
}
|
||||
@@ -70,16 +73,18 @@ impl 笔 {
|
||||
|
||||
if let Some(ref 筆) = 筆 {
|
||||
if let (Some(ref 高_k), Some(ref 低_k)) = (&实际高点, &实际低点) {
|
||||
let 原始数量 =
|
||||
1 + (低_k.标的K线.序号 - 高_k.标的K线.序号).unsigned_abs() as usize;
|
||||
let 原始数量 = 1
|
||||
+ (低_k.标的K线.read().unwrap().序号
|
||||
- 高_k.标的K线.read().unwrap().序号)
|
||||
.unsigned_abs() as usize;
|
||||
// 向上笔
|
||||
if 筆.方向().是否向上() && 低_k.低 < 筆.低() {
|
||||
if 筆.方向().是否向上() && 低_k.低.get() < 筆.低() {
|
||||
if 原始数量 >= 配置.笔弱化_原始数量 as usize {
|
||||
return 配置.笔内元素数量 as usize;
|
||||
}
|
||||
}
|
||||
// 向下笔
|
||||
if 筆.方向().是否向下() && 低_k.低 > 筆.高() {
|
||||
if 筆.方向().是否向下() && 低_k.低.get() > 筆.高() {
|
||||
if 原始数量 >= 配置.笔弱化_原始数量 as usize {
|
||||
return 配置.笔内元素数量 as usize;
|
||||
}
|
||||
@@ -92,147 +97,182 @@ impl 笔 {
|
||||
}
|
||||
|
||||
/// 次高 — 排除最高值后的次高点
|
||||
pub fn 次高(缠K序列: &[Rc<缠论K线>], 取舍: bool) -> Option<Rc<缠论K线>> {
|
||||
pub fn 次高(缠K序列: &[Arc<缠论K线>], 取舍: bool) -> Option<Arc<缠论K线>> {
|
||||
if 缠K序列.len() < 2 {
|
||||
return 缠K序列.first().cloned();
|
||||
}
|
||||
let max_高 = 缠K序列
|
||||
.iter()
|
||||
.map(|k| k.高)
|
||||
.map(|k| k.高.get())
|
||||
.fold(f64::NEG_INFINITY, f64::max);
|
||||
// 排除最高值
|
||||
let filtered: Vec<&Rc<缠论K线>> = 缠K序列.iter().filter(|k| k.高 != max_高).collect();
|
||||
let filtered: Vec<&Arc<缠论K线>> =
|
||||
缠K序列.iter().filter(|k| k.高.get() != max_高).collect();
|
||||
if filtered.is_empty() {
|
||||
return 缠K序列.first().cloned();
|
||||
}
|
||||
// 筛选次高值
|
||||
let second_高 = filtered
|
||||
.iter()
|
||||
.map(|k| k.高)
|
||||
.map(|k| k.高.get())
|
||||
.fold(f64::NEG_INFINITY, f64::max);
|
||||
let mut candidates: Vec<&Rc<缠论K线>> = filtered
|
||||
let mut candidates: Vec<&Arc<缠论K线>> = filtered
|
||||
.iter()
|
||||
.filter(|k| k.高 == second_高)
|
||||
.filter(|k| k.高.get() == second_高)
|
||||
.copied()
|
||||
.collect();
|
||||
// 按时间戳排序
|
||||
candidates.sort_by(|a, b| a.时间戳.cmp(&b.时间戳));
|
||||
candidates.sort_by(|a, b| {
|
||||
a.时间戳
|
||||
.load(Ordering::Relaxed)
|
||||
.cmp(&b.时间戳.load(Ordering::Relaxed))
|
||||
});
|
||||
if 取舍 {
|
||||
Some(Rc::clone(candidates[candidates.len() - 1]))
|
||||
Some(Arc::clone(candidates[candidates.len() - 1]))
|
||||
} else {
|
||||
Some(Rc::clone(candidates[0]))
|
||||
Some(Arc::clone(candidates[0]))
|
||||
}
|
||||
}
|
||||
|
||||
/// 次低 — 排除最低值后的次低点
|
||||
pub fn 次低(缠K序列: &[Rc<缠论K线>], 取舍: bool) -> Option<Rc<缠论K线>> {
|
||||
pub fn 次低(缠K序列: &[Arc<缠论K线>], 取舍: bool) -> Option<Arc<缠论K线>> {
|
||||
if 缠K序列.len() < 2 {
|
||||
return 缠K序列.first().cloned();
|
||||
}
|
||||
let min_低 = 缠K序列.iter().map(|k| k.低).fold(f64::INFINITY, f64::min);
|
||||
let min_低 = 缠K序列
|
||||
.iter()
|
||||
.map(|k| k.低.get())
|
||||
.fold(f64::INFINITY, f64::min);
|
||||
// 排除最低值
|
||||
let filtered: Vec<&Rc<缠论K线>> = 缠K序列.iter().filter(|k| k.低 != min_低).collect();
|
||||
let filtered: Vec<&Arc<缠论K线>> =
|
||||
缠K序列.iter().filter(|k| k.低.get() != 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
|
||||
let second_低 = filtered
|
||||
.iter()
|
||||
.filter(|k| k.低 == second_低)
|
||||
.map(|k| k.低.get())
|
||||
.fold(f64::INFINITY, f64::min);
|
||||
let mut candidates: Vec<&Arc<缠论K线>> = filtered
|
||||
.iter()
|
||||
.filter(|k| k.低.get() == second_低)
|
||||
.copied()
|
||||
.collect();
|
||||
// 按时间戳排序
|
||||
candidates.sort_by(|a, b| a.时间戳.cmp(&b.时间戳));
|
||||
candidates.sort_by(|a, b| {
|
||||
a.时间戳
|
||||
.load(Ordering::Relaxed)
|
||||
.cmp(&b.时间戳.load(Ordering::Relaxed))
|
||||
});
|
||||
if 取舍 {
|
||||
Some(Rc::clone(candidates[candidates.len() - 1]))
|
||||
Some(Arc::clone(candidates[candidates.len() - 1]))
|
||||
} else {
|
||||
Some(Rc::clone(candidates[0]))
|
||||
Some(Arc::clone(candidates[0]))
|
||||
}
|
||||
}
|
||||
|
||||
/// 实际高点
|
||||
pub fn 实际高点(缠K序列: &[Rc<缠论K线>], 取舍: bool) -> Option<Rc<缠论K线>> {
|
||||
pub fn 实际高点(缠K序列: &[Arc<缠论K线>], 取舍: bool) -> Option<Arc<缠论K线>> {
|
||||
if 缠K序列.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let max_高 = 缠K序列
|
||||
.iter()
|
||||
.map(|k| k.高)
|
||||
.map(|k| k.高.get())
|
||||
.fold(f64::NEG_INFINITY, f64::max);
|
||||
let mut candidates: Vec<&Rc<缠论K线>> = 缠K序列.iter().filter(|k| k.高 == max_高).collect();
|
||||
let mut candidates: Vec<&Arc<缠论K线>> =
|
||||
缠K序列.iter().filter(|k| k.高.get() == max_高).collect();
|
||||
if candidates.is_empty() {
|
||||
return Some(Rc::clone(&缠K序列[0]));
|
||||
return Some(Arc::clone(&缠K序列[0]));
|
||||
}
|
||||
// 按时间戳排序
|
||||
candidates.sort_by(|a, b| a.时间戳.cmp(&b.时间戳));
|
||||
candidates.sort_by(|a, b| {
|
||||
a.时间戳
|
||||
.load(Ordering::Relaxed)
|
||||
.cmp(&b.时间戳.load(Ordering::Relaxed))
|
||||
});
|
||||
if 取舍 {
|
||||
Some(Rc::clone(candidates[candidates.len() - 1]))
|
||||
Some(Arc::clone(candidates[candidates.len() - 1]))
|
||||
} else {
|
||||
Some(Rc::clone(candidates[0]))
|
||||
Some(Arc::clone(candidates[0]))
|
||||
}
|
||||
}
|
||||
|
||||
/// 实际低点
|
||||
pub fn 实际低点(缠K序列: &[Rc<缠论K线>], 取舍: bool) -> Option<Rc<缠论K线>> {
|
||||
pub fn 实际低点(缠K序列: &[Arc<缠论K线>], 取舍: bool) -> Option<Arc<缠论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();
|
||||
let min_低 = 缠K序列
|
||||
.iter()
|
||||
.map(|k| k.低.get())
|
||||
.fold(f64::INFINITY, f64::min);
|
||||
let mut candidates: Vec<&Arc<缠论K线>> =
|
||||
缠K序列.iter().filter(|k| k.低.get() == min_低).collect();
|
||||
if candidates.is_empty() {
|
||||
return Some(Rc::clone(&缠K序列[0]));
|
||||
return Some(Arc::clone(&缠K序列[0]));
|
||||
}
|
||||
// 按时间戳排序
|
||||
candidates.sort_by(|a, b| a.时间戳.cmp(&b.时间戳));
|
||||
candidates.sort_by(|a, b| {
|
||||
a.时间戳
|
||||
.load(Ordering::Relaxed)
|
||||
.cmp(&b.时间戳.load(Ordering::Relaxed))
|
||||
});
|
||||
if 取舍 {
|
||||
Some(Rc::clone(candidates[candidates.len() - 1]))
|
||||
Some(Arc::clone(candidates[candidates.len() - 1]))
|
||||
} else {
|
||||
Some(Rc::clone(candidates[0]))
|
||||
Some(Arc::clone(candidates[0]))
|
||||
}
|
||||
}
|
||||
|
||||
/// 判断笔的相对关系是否合理
|
||||
pub fn 相对关系(筆: &虚线, 配置: &缠论配置) -> bool {
|
||||
let 文分型 = &筆.文;
|
||||
let 武分型 = &筆.武;
|
||||
let 武分型 = 筆.武.read().unwrap();
|
||||
|
||||
let 相对关系 = if 配置.笔内起始分型包含整笔 {
|
||||
let 文中_rc = Rc::clone(&文分型.中);
|
||||
let 武中_rc = Rc::clone(&武分型.中);
|
||||
let 文_元素: [Option<&Rc<缠论K线>>; 3] =
|
||||
let 文中_rc = Arc::clone(&文分型.中);
|
||||
let 武中_rc = Arc::clone(&武分型.中);
|
||||
let 文_元素: [Option<&Arc<缠论K线>>; 3] =
|
||||
[文分型.左.as_ref(), Some(&文中_rc), 文分型.右.as_ref()];
|
||||
let 有效序列: Vec<&Rc<缠论K线>> = 文_元素.iter().filter_map(|x| *x).collect();
|
||||
let 有效序列: Vec<&Arc<缠论K线>> = 文_元素.iter().filter_map(|x| *x).collect();
|
||||
let 文高 = 有效序列
|
||||
.iter()
|
||||
.map(|k| k.高)
|
||||
.map(|k| k.高.get())
|
||||
.fold(f64::NEG_INFINITY, f64::max);
|
||||
let 文低 = 有效序列.iter().map(|k| k.低).fold(f64::INFINITY, f64::min);
|
||||
let 文低 = 有效序列
|
||||
.iter()
|
||||
.map(|k| k.低.get())
|
||||
.fold(f64::INFINITY, f64::min);
|
||||
|
||||
let 武_右: Option<&Rc<缠论K线>> = if 配置.笔内起始分型包含整笔_包括右 {
|
||||
let 武_右: Option<&Arc<缠论K线>> = if 配置.笔内起始分型包含整笔_包括右 {
|
||||
武分型.右.as_ref()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let 武_元素: [Option<&Rc<缠论K线>>; 3] = [武分型.左.as_ref(), Some(&武中_rc), 武_右];
|
||||
let 有效序列: Vec<&Rc<缠论K线>> = 武_元素.iter().filter_map(|x| *x).collect();
|
||||
let 武_元素: [Option<&Arc<缠论K线>>; 3] = [武分型.左.as_ref(), Some(&武中_rc), 武_右];
|
||||
let 有效序列: Vec<&Arc<缠论K线>> = 武_元素.iter().filter_map(|x| *x).collect();
|
||||
let 武高 = 有效序列
|
||||
.iter()
|
||||
.map(|k| k.高)
|
||||
.map(|k| k.高.get())
|
||||
.fold(f64::NEG_INFINITY, f64::max);
|
||||
let 武低 = 有效序列.iter().map(|k| k.低).fold(f64::INFINITY, f64::min);
|
||||
let 武低 = 有效序列
|
||||
.iter()
|
||||
.map(|k| k.低.get())
|
||||
.fold(f64::INFINITY, f64::min);
|
||||
|
||||
crate::types::相对方向::分析(文高, 文低, 武高, 武低)
|
||||
} else {
|
||||
let 相对关系 = crate::types::相对方向::分析(
|
||||
文分型.中.高,
|
||||
文分型.中.低,
|
||||
武分型.中.高,
|
||||
武分型.中.低,
|
||||
文分型.中.高.get(),
|
||||
文分型.中.低.get(),
|
||||
武分型.中.高.get(),
|
||||
武分型.中.低.get(),
|
||||
);
|
||||
if 配置.笔内原始K线包含整笔 {
|
||||
let 文标的 = &文分型.中.标的K线;
|
||||
let 武标的 = &武分型.中.标的K线;
|
||||
let 文标的 = 文分型.中.标的K线.read().unwrap();
|
||||
let 武标的 = 武分型.中.标的K线.read().unwrap();
|
||||
if crate::types::相对方向::分析(文标的.高, 文标的.低, 武标的.高, 武标的.低)
|
||||
.是否包含()
|
||||
{
|
||||
@@ -249,43 +289,46 @@ impl 笔 {
|
||||
}
|
||||
|
||||
/// 以文会友 — 根据起点分型找笔
|
||||
pub fn 以文会友(笔序列: &[Rc<虚线>], 文: &Rc<分型>) -> Option<Rc<虚线>> {
|
||||
pub fn 以文会友(笔序列: &[Arc<虚线>], 文: &Arc<分型>) -> Option<Arc<虚线>> {
|
||||
笔序列
|
||||
.iter()
|
||||
.find(|b| Rc::as_ptr(&b.文) == Rc::as_ptr(文))
|
||||
.find(|b| Arc::as_ptr(&b.文) == Arc::as_ptr(文))
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/// 以武会友 — 根据终点分型找笔
|
||||
pub fn 以武会友(笔序列: &[Rc<虚线>], 武: &Rc<分型>) -> Option<Rc<虚线>> {
|
||||
pub fn 以武会友(笔序列: &[Arc<虚线>], 武: &Arc<分型>) -> Option<Arc<虚线>> {
|
||||
笔序列
|
||||
.iter()
|
||||
.rev()
|
||||
.find(|b| Rc::as_ptr(&b.武) == Rc::as_ptr(武))
|
||||
.find(|b| Arc::as_ptr(&*b.武.read().unwrap()) == Arc::as_ptr(武))
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/// 根据缠K找对应的笔
|
||||
pub fn 根据缠K找笔(
|
||||
笔序列: &[Rc<虚线>],
|
||||
缠K: &Rc<缠论K线>,
|
||||
笔序列: &[Arc<虚线>],
|
||||
缠K: &Arc<缠论K线>,
|
||||
偏移: i64,
|
||||
) -> Option<Rc<虚线>> {
|
||||
) -> Option<Arc<虚线>> {
|
||||
// Python iterates in reverse: for 筆 in 笔序列[::-1]
|
||||
for b in 笔序列.iter().rev() {
|
||||
// Python: 筆.文.中.序号 - 偏移 <= 缠K.序号 <= 筆.武.中.序号
|
||||
if b.文.中.序号 - 偏移 <= 缠K.序号 && 缠K.序号 <= b.武.中.序号 {
|
||||
return Some(Rc::clone(b));
|
||||
// Python: 筆.文.中.序号 - 偏移 <= 缠K.序号.load(Ordering::Relaxed) <= 筆.武.read().unwrap().中.序号
|
||||
if b.文.中.序号.load(Ordering::Relaxed) - 偏移 <= 缠K.序号.load(Ordering::Relaxed)
|
||||
&& 缠K.序号.load(Ordering::Relaxed)
|
||||
<= b.武.read().unwrap().中.序号.load(Ordering::Relaxed)
|
||||
{
|
||||
return Some(Arc::clone(b));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// 从分型序列中弹出最后一个分型和对应的笔
|
||||
fn 弹出旧笔(分型序列: &mut Vec<Rc<分型>>, 笔序列: &mut Vec<Rc<虚线>>) {
|
||||
fn 弹出旧笔(分型序列: &mut Vec<Arc<分型>>, 笔序列: &mut Vec<Arc<虚线>>) {
|
||||
分型序列.pop();
|
||||
if !笔序列.is_empty() {
|
||||
// Python sets旧笔.有效性 = False; with Rc we just drop the笔
|
||||
// Python sets旧笔.有效性.store(False, Ordering::Relaxed); with Rc we just drop the笔
|
||||
笔序列.pop();
|
||||
}
|
||||
}
|
||||
@@ -294,19 +337,19 @@ impl 笔 {
|
||||
///
|
||||
/// 返回: 递归层次数
|
||||
pub fn 分析(
|
||||
初始分型: Rc<分型>,
|
||||
分型序列: &mut Vec<Rc<分型>>,
|
||||
笔序列: &mut Vec<Rc<虚线>>,
|
||||
缠K序列: &[Rc<缠论K线>],
|
||||
_普K序列: &[Rc<K线>],
|
||||
初始分型: Arc<分型>,
|
||||
分型序列: &mut Vec<Arc<分型>>,
|
||||
笔序列: &mut Vec<Arc<虚线>>,
|
||||
缠K序列: &[Arc<缠论K线>],
|
||||
_普K序列: &[Arc<K线>],
|
||||
配置: &缠论配置,
|
||||
) -> i64 {
|
||||
enum 栈项 {
|
||||
分型(Rc<分型>, i64),
|
||||
分型(Arc<分型>, i64),
|
||||
/// 修复错过笔哨兵: 若临时分型被接受为最后一个元素,则扫描武将之后的所有分型
|
||||
修复错过笔 {
|
||||
临时分型: Rc<分型>,
|
||||
武将缠K: Rc<缠论K线>,
|
||||
临时分型: Arc<分型>,
|
||||
武将缠K: Arc<缠论K线>,
|
||||
层次: i64,
|
||||
},
|
||||
}
|
||||
@@ -325,20 +368,20 @@ impl 笔 {
|
||||
// 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 Arc::as_ptr(last_fx) == Arc::as_ptr(&临时分型) {
|
||||
if let Some(武_idx) = 缠K序列
|
||||
.iter()
|
||||
.position(|k| Rc::as_ptr(k) == Rc::as_ptr(&武将缠K))
|
||||
.position(|k| Arc::as_ptr(k) == Arc::as_ptr(&武将缠K))
|
||||
{
|
||||
let mut 错过: Vec<Rc<分型>> = Vec::new();
|
||||
let mut 错过: Vec<Arc<分型>> = Vec::new();
|
||||
for ck in &缠K序列[武_idx..] {
|
||||
if ck.分型 == Some(分型结构::底)
|
||||
|| ck.分型 == Some(分型结构::顶)
|
||||
if *ck.分型.read().unwrap() == Some(分型结构::底)
|
||||
|| *ck.分型.read().unwrap() == Some(分型结构::顶)
|
||||
{
|
||||
if let Some(fx) =
|
||||
分型::从缠K序列中获取分型(缠K序列, ck)
|
||||
{
|
||||
错过.push(Rc::new(fx));
|
||||
错过.push(Arc::new(fx));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -369,7 +412,7 @@ impl 笔 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let 之前分型 = Rc::clone(分型序列.last().unwrap());
|
||||
let 之前分型 = Arc::clone(分型序列.last().unwrap());
|
||||
|
||||
// Python line 2330-2335: 清理无效数据
|
||||
if 之前分型.时间戳 == 当前分型.时间戳
|
||||
@@ -384,10 +427,13 @@ impl 笔 {
|
||||
}
|
||||
}
|
||||
|
||||
let 之前分型 = Rc::clone(分型序列.last().unwrap());
|
||||
let 之前分型 = Arc::clone(分型序列.last().unwrap());
|
||||
|
||||
// Python line 2338: 时序检查 — skip out-of-order fractals
|
||||
if 之前分型.时间戳 > 当前分型.时间戳 && 之前分型.中.序号 - 当前分型.中.序号 > 1
|
||||
if 之前分型.时间戳 > 当前分型.时间戳
|
||||
&& 之前分型.中.序号.load(Ordering::Relaxed)
|
||||
- 当前分型.中.序号.load(Ordering::Relaxed)
|
||||
> 1
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -395,7 +441,9 @@ impl 笔 {
|
||||
// Python line 2343-2348: 笔弱化模式
|
||||
if 配置.笔弱化 && !笔序列.is_empty() {
|
||||
let 前一笔 = 笔序列.last().unwrap();
|
||||
let 前一笔缠K数 = 前一笔.武.中.序号 - 前一笔.文.中.序号 + 1;
|
||||
let 前一笔缠K数 = 前一笔.武.read().unwrap().中.序号.load(Ordering::Relaxed)
|
||||
- 前一笔.文.中.序号.load(Ordering::Relaxed)
|
||||
+ 1;
|
||||
if 前一笔缠K数 == 3 {
|
||||
let 破位 = (前一笔.方向().是否向上()
|
||||
&& 前一笔.低() > 当前分型.分型特征值
|
||||
@@ -412,16 +460,16 @@ impl 笔 {
|
||||
}
|
||||
|
||||
// Re-read之前分型 again after笔弱化 pop
|
||||
let 之前分型 = Rc::clone(分型序列.last().unwrap());
|
||||
let 之前分型 = Arc::clone(分型序列.last().unwrap());
|
||||
|
||||
// Python line 2350: 分型结构相反 → 可能成笔
|
||||
if 之前分型.结构 != 当前分型.结构 {
|
||||
let 文_idx = 缠K序列
|
||||
.iter()
|
||||
.position(|k| Rc::as_ptr(k) == Rc::as_ptr(&之前分型.中));
|
||||
.position(|k| Arc::as_ptr(k) == Arc::as_ptr(&之前分型.中));
|
||||
let 武_idx = 缠K序列
|
||||
.iter()
|
||||
.position(|k| Rc::as_ptr(k) == Rc::as_ptr(&当前分型.中));
|
||||
.position(|k| Arc::as_ptr(k) == Arc::as_ptr(&当前分型.中));
|
||||
|
||||
if let (Some(文_idx), Some(武_idx)) = (文_idx, 武_idx) {
|
||||
let 基础序列 = &缠K序列[文_idx..=武_idx];
|
||||
@@ -436,12 +484,12 @@ impl 笔 {
|
||||
|
||||
// Python line 2359-2367: 文官 != 之前分型.中 → adjust
|
||||
if let Some(ref 文官_k) = 文官 {
|
||||
if Rc::as_ptr(文官_k) != Rc::as_ptr(&之前分型.中) {
|
||||
if Arc::as_ptr(文官_k) != Arc::as_ptr(&之前分型.中) {
|
||||
if let Some(临时分型) =
|
||||
分型::从缠K序列中获取分型(缠K序列, 文官_k)
|
||||
{
|
||||
栈.push(栈项::分型(当前分型, 递归层次));
|
||||
栈.push(栈项::分型(Rc::new(临时分型), 递归层次 + 1));
|
||||
栈.push(栈项::分型(Arc::new(临时分型), 递归层次 + 1));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -455,16 +503,16 @@ impl 笔 {
|
||||
_ => Self::实际高点(基础序列, 配置.笔内相同终点取舍),
|
||||
};
|
||||
|
||||
let 新笔 = Rc::new(虚线::创建笔(
|
||||
Rc::clone(&之前分型),
|
||||
Rc::clone(&当前分型),
|
||||
let 新笔 = Arc::new(虚线::创建笔(
|
||||
Arc::clone(&之前分型),
|
||||
Arc::clone(&当前分型),
|
||||
true,
|
||||
));
|
||||
|
||||
// Python line 2374-2376: 相对关系 and武将 matches
|
||||
if Self::相对关系(&新笔, 配置) {
|
||||
if let Some(ref 武将_k) = 武将 {
|
||||
if Rc::as_ptr(武将_k) == Rc::as_ptr(&当前分型.中) {
|
||||
if Arc::as_ptr(武将_k) == Arc::as_ptr(&当前分型.中) {
|
||||
Self::_添加新笔递归(分型序列, 笔序列, 当前分型, 新笔);
|
||||
continue;
|
||||
}
|
||||
@@ -478,7 +526,7 @@ impl 笔 {
|
||||
_ => Self::次高(基础序列, 配置.笔内相同终点取舍),
|
||||
};
|
||||
if let Some(ref 武将_k) = 武将 {
|
||||
if Rc::as_ptr(武将_k) == Rc::as_ptr(&当前分型.中)
|
||||
if Arc::as_ptr(武将_k) == Arc::as_ptr(&当前分型.中)
|
||||
&& Self::相对关系(&新笔, 配置)
|
||||
{
|
||||
Self::_添加新笔递归(分型序列, 笔序列, 当前分型, 新笔);
|
||||
@@ -492,7 +540,7 @@ impl 笔 {
|
||||
if let Some(临时分型) =
|
||||
分型::从缠K序列中获取分型(缠K序列, 右)
|
||||
{
|
||||
栈.push(栈项::分型(Rc::new(临时分型), 递归层次 + 1));
|
||||
栈.push(栈项::分型(Arc::new(临时分型), 递归层次 + 1));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -509,7 +557,7 @@ impl 笔 {
|
||||
};
|
||||
|
||||
if 更强 {
|
||||
let 被替换分型 = Rc::clone(&之前分型);
|
||||
let 被替换分型 = Arc::clone(&之前分型);
|
||||
Self::弹出旧笔(分型序列, 笔序列);
|
||||
|
||||
if let Some(k线序列) = 缠论K线::截取(缠K序列, &被替换分型.中, &当前分型.中)
|
||||
@@ -525,14 +573,14 @@ impl 笔 {
|
||||
if let Some(临时分型) =
|
||||
分型::从缠K序列中获取分型(缠K序列, 武将_k)
|
||||
{
|
||||
let 临时分型_rc = Rc::new(临时分型);
|
||||
let 临时分型_rc = Arc::new(临时分型);
|
||||
|
||||
if !分型序列.is_empty() {
|
||||
// Push in reverse processing order (LIFO):
|
||||
栈.push(栈项::分型(Rc::clone(&当前分型), 递归层次 + 2));
|
||||
栈.push(栈项::分型(Arc::clone(&当前分型), 递归层次 + 2));
|
||||
栈.push(栈项::修复错过笔 {
|
||||
临时分型: Rc::clone(&临时分型_rc),
|
||||
武将缠K: Rc::clone(武将_k),
|
||||
临时分型: Arc::clone(&临时分型_rc),
|
||||
武将缠K: Arc::clone(武将_k),
|
||||
层次: 递归层次 + 1,
|
||||
});
|
||||
栈.push(栈项::分型(临时分型_rc, 递归层次 + 1));
|
||||
@@ -561,11 +609,11 @@ impl 笔 {
|
||||
///
|
||||
/// 返回: 递归层次数
|
||||
pub fn 分析递归(
|
||||
当前分型: Rc<分型>,
|
||||
分型序列: &mut Vec<Rc<分型>>,
|
||||
笔序列: &mut Vec<Rc<虚线>>,
|
||||
缠K序列: &[Rc<缠论K线>],
|
||||
_普K序列: &[Rc<K线>],
|
||||
当前分型: Arc<分型>,
|
||||
分型序列: &mut Vec<Arc<分型>>,
|
||||
笔序列: &mut Vec<Arc<虚线>>,
|
||||
缠K序列: &[Arc<缠论K线>],
|
||||
_普K序列: &[Arc<K线>],
|
||||
递归层次: i64,
|
||||
配置: &缠论配置,
|
||||
) -> i64 {
|
||||
@@ -588,7 +636,7 @@ impl 笔 {
|
||||
}
|
||||
|
||||
// Python line 2329-2335: 清理无效数据
|
||||
let 之前分型 = Rc::clone(分型序列.last().unwrap());
|
||||
let 之前分型 = Arc::clone(分型序列.last().unwrap());
|
||||
if 之前分型.时间戳 == 当前分型.时间戳
|
||||
|| matches!(之前分型.结构, 分型结构::上 | 分型结构::下)
|
||||
{
|
||||
@@ -602,8 +650,10 @@ impl 笔 {
|
||||
}
|
||||
|
||||
// Python line 2337-2341: 时序检查
|
||||
let 之前分型 = Rc::clone(分型序列.last().unwrap());
|
||||
if 之前分型.时间戳 > 当前分型.时间戳 && 之前分型.中.序号 - 当前分型.中.序号 > 1
|
||||
let 之前分型 = Arc::clone(分型序列.last().unwrap());
|
||||
if 之前分型.时间戳 > 当前分型.时间戳
|
||||
&& 之前分型.中.序号.load(Ordering::Relaxed) - 当前分型.中.序号.load(Ordering::Relaxed)
|
||||
> 1
|
||||
{
|
||||
println!("时序错误-{}, {}, {}", 递归层次, 之前分型, 当前分型);
|
||||
return 递归层次;
|
||||
@@ -612,7 +662,9 @@ impl 笔 {
|
||||
// Python line 2343-2348: 笔弱化模式
|
||||
if 配置.笔弱化 && !笔序列.is_empty() {
|
||||
let 前一笔 = 笔序列.last().unwrap();
|
||||
let 前一笔缠K数 = 前一笔.武.中.序号 - 前一笔.文.中.序号 + 1;
|
||||
let 前一笔缠K数 = 前一笔.武.read().unwrap().中.序号.load(Ordering::Relaxed)
|
||||
- 前一笔.文.中.序号.load(Ordering::Relaxed)
|
||||
+ 1;
|
||||
if 前一笔缠K数 == 3 {
|
||||
let 破位 = (前一笔.方向().是否向上()
|
||||
&& 前一笔.低() > 当前分型.分型特征值
|
||||
@@ -636,13 +688,13 @@ impl 笔 {
|
||||
}
|
||||
|
||||
// Python line 2350: 分型结构相反 → 可能成笔
|
||||
let 之前分型 = Rc::clone(分型序列.last().unwrap());
|
||||
let 之前分型 = Arc::clone(分型序列.last().unwrap());
|
||||
if 之前分型.结构 != 当前分型.结构 {
|
||||
if let Some(基础序列) = 缠论K线::截取(缠K序列, &之前分型.中, &当前分型.中)
|
||||
{
|
||||
let 当前笔 = Rc::new(虚线::创建笔(
|
||||
Rc::clone(&之前分型),
|
||||
Rc::clone(&当前分型),
|
||||
let 当前笔 = Arc::new(虚线::创建笔(
|
||||
Arc::clone(&之前分型),
|
||||
Arc::clone(&当前分型),
|
||||
true,
|
||||
));
|
||||
|
||||
@@ -656,12 +708,12 @@ impl 笔 {
|
||||
|
||||
// Python line 2359-2367: 文官调整
|
||||
if let Some(ref 文官_k) = 文官 {
|
||||
if Rc::as_ptr(文官_k) != Rc::as_ptr(&之前分型.中) {
|
||||
if Arc::as_ptr(文官_k) != Arc::as_ptr(&之前分型.中) {
|
||||
if let Some(临时分型) =
|
||||
分型::从缠K序列中获取分型(缠K序列, 文官_k)
|
||||
{
|
||||
let 递归层次 = Self::分析递归(
|
||||
Rc::new(临时分型),
|
||||
Arc::new(临时分型),
|
||||
分型序列,
|
||||
笔序列,
|
||||
缠K序列,
|
||||
@@ -690,7 +742,7 @@ impl 笔 {
|
||||
|
||||
if Self::相对关系(&当前笔, 配置) {
|
||||
if let Some(ref 武将_k) = 武将 {
|
||||
if Rc::as_ptr(武将_k) == Rc::as_ptr(&当前分型.中) {
|
||||
if Arc::as_ptr(武将_k) == Arc::as_ptr(&当前分型.中) {
|
||||
// 直接添加(对照 Python _添加新笔:直接 append)
|
||||
Self::_添加新笔递归(分型序列, 笔序列, 当前分型, 当前笔);
|
||||
return 递归层次;
|
||||
@@ -705,7 +757,7 @@ impl 笔 {
|
||||
_ => Self::次高(&基础序列, 配置.笔内相同终点取舍),
|
||||
};
|
||||
if let Some(ref 武将_k) = 武将 {
|
||||
if Rc::as_ptr(武将_k) == Rc::as_ptr(&当前分型.中)
|
||||
if Arc::as_ptr(武将_k) == Arc::as_ptr(&当前分型.中)
|
||||
&& Self::相对关系(&当前笔, 配置)
|
||||
{
|
||||
Self::_添加新笔递归(分型序列, 笔序列, 当前分型, 当前笔);
|
||||
@@ -719,7 +771,7 @@ impl 笔 {
|
||||
if let Some(临时分型) = 分型::从缠K序列中获取分型(缠K序列, 右)
|
||||
{
|
||||
return Self::分析递归(
|
||||
Rc::new(临时分型),
|
||||
Arc::new(临时分型),
|
||||
分型序列,
|
||||
笔序列,
|
||||
缠K序列,
|
||||
@@ -743,7 +795,7 @@ impl 笔 {
|
||||
|
||||
if 更强 {
|
||||
// 保存被弹出的之前分型(用于修复错过笔的范围计算)
|
||||
let 被替换分型 = Rc::clone(&之前分型);
|
||||
let 被替换分型 = Arc::clone(&之前分型);
|
||||
Self::弹出旧笔(分型序列, 笔序列);
|
||||
|
||||
if let Some(k线序列) = 缠论K线::截取(缠K序列, &被替换分型.中, &当前分型.中)
|
||||
@@ -757,11 +809,11 @@ impl 笔 {
|
||||
if let Some(临时分型) =
|
||||
分型::从缠K序列中获取分型(缠K序列, 武将_k)
|
||||
{
|
||||
let 临时分型_rc = Rc::new(临时分型);
|
||||
let 临时分型_rc = Arc::new(临时分型);
|
||||
|
||||
if !分型序列.is_empty() {
|
||||
let mut 递归层次 = Self::分析递归(
|
||||
Rc::clone(&临时分型_rc),
|
||||
Arc::clone(&临时分型_rc),
|
||||
分型序列,
|
||||
笔序列,
|
||||
缠K序列,
|
||||
@@ -772,25 +824,25 @@ impl 笔 {
|
||||
|
||||
// 修复错过的笔: 扫描武将之后的所有分型
|
||||
if !分型序列.is_empty()
|
||||
&& Rc::as_ptr(分型序列.last().unwrap())
|
||||
== Rc::as_ptr(&临时分型_rc)
|
||||
&& Arc::as_ptr(分型序列.last().unwrap())
|
||||
== Arc::as_ptr(&临时分型_rc)
|
||||
{
|
||||
if let Some(武_idx) = 缠K序列
|
||||
.iter()
|
||||
.position(|k| Rc::as_ptr(k) == Rc::as_ptr(武将_k))
|
||||
.position(|k| Arc::as_ptr(k) == Arc::as_ptr(武将_k))
|
||||
{
|
||||
for ck in &缠K序列[武_idx..] {
|
||||
if ck.分型 == Some(分型结构::底)
|
||||
|| ck.分型 == Some(分型结构::顶)
|
||||
if *ck.分型.read().unwrap() == Some(分型结构::底)
|
||||
|| *ck.分型.read().unwrap() == Some(分型结构::顶)
|
||||
{
|
||||
if let Some(错过分型) =
|
||||
分型::从缠K序列中获取分型(
|
||||
缠K序列, ck,
|
||||
)
|
||||
{
|
||||
let 错过分型_rc = Rc::new(错过分型);
|
||||
let 错过分型_rc = Arc::new(错过分型);
|
||||
递归层次 = Self::分析递归(
|
||||
Rc::clone(&错过分型_rc),
|
||||
Arc::clone(&错过分型_rc),
|
||||
分型序列,
|
||||
笔序列,
|
||||
缠K序列,
|
||||
@@ -839,17 +891,20 @@ impl 笔 {
|
||||
|
||||
/// 添加新笔到序列(递归版本 — 直接追加,对应 Python _添加新笔)
|
||||
fn _添加新笔递归(
|
||||
分型序列: &mut Vec<Rc<分型>>,
|
||||
笔序列: &mut Vec<Rc<虚线>>,
|
||||
新分型: Rc<分型>,
|
||||
mut 新笔: Rc<虚线>,
|
||||
分型序列: &mut Vec<Arc<分型>>,
|
||||
笔序列: &mut Vec<Arc<虚线>>,
|
||||
新分型: Arc<分型>,
|
||||
mut 新笔: Arc<虚线>,
|
||||
) {
|
||||
分型序列.push(新分型);
|
||||
if !笔序列.is_empty() {
|
||||
let seg = Rc::make_mut(&mut 新笔);
|
||||
seg.序号 = 笔序列.last().unwrap().序号 + 1;
|
||||
if seg.武.左.is_none() && seg.武.右.is_none() {
|
||||
seg.有效性 = false;
|
||||
let seg = Arc::make_mut(&mut 新笔);
|
||||
seg.序号.store(
|
||||
笔序列.last().unwrap().序号.load(Ordering::Relaxed) + 1,
|
||||
Ordering::Relaxed,
|
||||
);
|
||||
if seg.武.read().unwrap().左.is_none() && seg.武.read().unwrap().右.is_none() {
|
||||
seg.有效性.store(false, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
笔序列.push(新笔);
|
||||
@@ -867,7 +922,8 @@ impl 笔 {
|
||||
Self::实际高点(&基础序列, false),
|
||||
Self::实际低点(&基础序列, 配置.笔内相同终点取舍),
|
||||
) {
|
||||
if Rc::ptr_eq(&筆.文.中, &实际高) && Rc::ptr_eq(&筆.武.中, &实际低)
|
||||
if Arc::ptr_eq(&筆.文.中, &实际高)
|
||||
&& Arc::ptr_eq(&筆.武.read().unwrap().中, &实际低)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -878,7 +934,8 @@ impl 笔 {
|
||||
Self::实际低点(&基础序列, false),
|
||||
Self::实际高点(&基础序列, 配置.笔内相同终点取舍),
|
||||
) {
|
||||
if Rc::ptr_eq(&筆.文.中, &实际低) && Rc::ptr_eq(&筆.武.中, &实际高)
|
||||
if Arc::ptr_eq(&筆.文.中, &实际低)
|
||||
&& Arc::ptr_eq(&筆.武.read().unwrap().中, &实际高)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -891,7 +948,7 @@ impl 笔 {
|
||||
/// 获取所有停顿位置 — 在笔范围内找出所有能成笔的分型组合
|
||||
pub fn 获取所有停顿位置(筆: &虚线, 观察员: &观察者) -> Vec<虚线> {
|
||||
let mut 笔序列 = Vec::new();
|
||||
let 文 = Rc::clone(&筆.文);
|
||||
let 文 = Arc::clone(&筆.文);
|
||||
let 基础序列 = 筆.获取缠K序列(&观察员.缠论K线序列);
|
||||
|
||||
if 基础序列.len() < 5 {
|
||||
@@ -901,23 +958,30 @@ impl 笔 {
|
||||
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]);
|
||||
if *k.分型.read().unwrap() == Some(分型结构::顶) && 筆.方向() == 相对方向::向上
|
||||
{
|
||||
let 左 = Arc::clone(&基础序列[i - 1]);
|
||||
let 中 = Arc::clone(k);
|
||||
let 右 = Arc::clone(&基础序列[i + 1]);
|
||||
let 武 = 分型::new(Some(左), 中, Some(右));
|
||||
let mut 当前笔 = 虚线::创建笔(Rc::clone(&文), Rc::new(武), true);
|
||||
当前笔.序号 = 筆.序号;
|
||||
let 当前笔 = 虚线::创建笔(Arc::clone(&文), Arc::new(武), true);
|
||||
当前笔
|
||||
.序号
|
||||
.store(筆.序号.load(Ordering::Relaxed), Ordering::Relaxed);
|
||||
if Self::自检(&当前笔, 观察员) {
|
||||
笔序列.push(当前笔);
|
||||
}
|
||||
} else if k.分型 == Some(分型结构::底) && 筆.方向() == 相对方向::向下 {
|
||||
let 左 = Rc::clone(&基础序列[i - 1]);
|
||||
let 中 = Rc::clone(k);
|
||||
let 右 = Rc::clone(&基础序列[i + 1]);
|
||||
} else if *k.分型.read().unwrap() == Some(分型结构::底)
|
||||
&& 筆.方向() == 相对方向::向下
|
||||
{
|
||||
let 左 = Arc::clone(&基础序列[i - 1]);
|
||||
let 中 = Arc::clone(k);
|
||||
let 右 = Arc::clone(&基础序列[i + 1]);
|
||||
let 武 = 分型::new(Some(左), 中, Some(右));
|
||||
let mut 当前笔 = 虚线::创建笔(Rc::clone(&文), Rc::new(武), true);
|
||||
当前笔.序号 = 筆.序号;
|
||||
let 当前笔 = 虚线::创建笔(Arc::clone(&文), Arc::new(武), true);
|
||||
当前笔
|
||||
.序号
|
||||
.store(筆.序号.load(Ordering::Relaxed), Ordering::Relaxed);
|
||||
if Self::自检(&当前笔, 观察员) {
|
||||
笔序列.push(当前笔);
|
||||
}
|
||||
@@ -928,19 +992,19 @@ impl 笔 {
|
||||
}
|
||||
|
||||
/// 是否背驰过 — 判断笔是否在停顿位置出现过MACD趋向背驰
|
||||
pub fn 是否背驰过(当前筆: &虚线, 观察员: &观察者) -> Vec<Rc<缠论K线>> {
|
||||
pub fn 是否背驰过(当前筆: &虚线, 观察员: &观察者) -> Vec<Arc<缠论K线>> {
|
||||
let 停顿位置 = Self::获取所有停顿位置(当前筆, 观察员);
|
||||
let mut 结果 = Vec::new();
|
||||
|
||||
for 筆 in &停顿位置 {
|
||||
let k线范围 = K线::截取rc(
|
||||
&观察员.普通K线序列,
|
||||
&当前筆.文.中.标的K线,
|
||||
&当前筆.武.中.标的K线,
|
||||
&当前筆.文.中.标的K线.read().unwrap().clone(),
|
||||
&当前筆.武.read().unwrap().中.标的K线.read().unwrap().clone(),
|
||||
);
|
||||
let 背驰信号 = 虚线::计算K线序列MACD趋向背驰(&k线范围, 筆.方向());
|
||||
if 背驰信号.iter().all(|&x| x) {
|
||||
结果.push(Rc::clone(&筆.武.中));
|
||||
结果.push(Arc::clone(&筆.武.read().unwrap().中));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ use crate::config::缠论配置;
|
||||
use crate::kline::bar::K线;
|
||||
use crate::structure::dash_line::虚线;
|
||||
use crate::types::相对方向;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// 背驰分析 — 判断进入段和离开段之间是否存在背驰
|
||||
pub struct 背驰分析;
|
||||
@@ -35,12 +35,18 @@ impl 背驰分析 {
|
||||
/// MACD背驰 — MACD柱状线面积背驰
|
||||
/// 方式: "总"=阳+|阴|总面积, 其他=按进入段方向选阳或阴
|
||||
pub fn MACD背驰(
|
||||
进入段: &虚线, 离开段: &虚线, K线序列: &[Rc<K线>], 方式: &str
|
||||
进入段: &虚线, 离开段: &虚线, K线序列: &[Arc<K线>], 方式: &str
|
||||
) -> bool {
|
||||
let 进入MACD =
|
||||
Self::_获取MACD面积(K线序列, &进入段.文.中.标的K线, &进入段.武.中.标的K线);
|
||||
let 离开MACD =
|
||||
Self::_获取MACD面积(K线序列, &离开段.文.中.标的K线, &离开段.武.中.标的K线);
|
||||
let 进入MACD = Self::_获取MACD面积(
|
||||
K线序列,
|
||||
&*进入段.文.中.标的K线.read().unwrap(),
|
||||
&*进入段.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
);
|
||||
let 离开MACD = Self::_获取MACD面积(
|
||||
K线序列,
|
||||
&*离开段.文.中.标的K线.read().unwrap(),
|
||||
&*离开段.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
);
|
||||
|
||||
// 计算面积(绝对值求和)
|
||||
let 进入面积 = if 方式 == "总" {
|
||||
@@ -63,18 +69,18 @@ impl 背驰分析 {
|
||||
|
||||
/// 斜率背驰 — 价格斜率背驰
|
||||
pub fn 斜率背驰(进入段: &虚线, 离开段: &虚线) -> bool {
|
||||
let dx = (进入段.武.时间戳 - 进入段.文.时间戳) as f64;
|
||||
let dx = (进入段.武.read().unwrap().时间戳 - 进入段.文.时间戳) as f64;
|
||||
if dx == 0.0 {
|
||||
return false;
|
||||
}
|
||||
let dy = 进入段.武.分型特征值 - 进入段.文.分型特征值;
|
||||
let dy = 进入段.武.read().unwrap().分型特征值 - 进入段.文.分型特征值;
|
||||
let 进入斜率 = dy / dx;
|
||||
|
||||
let dx = (离开段.武.时间戳 - 离开段.文.时间戳) as f64;
|
||||
let dx = (离开段.武.read().unwrap().时间戳 - 离开段.文.时间戳) as f64;
|
||||
if dx == 0.0 {
|
||||
return false;
|
||||
}
|
||||
let dy = 离开段.武.分型特征值 - 离开段.文.分型特征值;
|
||||
let dy = 离开段.武.read().unwrap().分型特征值 - 离开段.文.分型特征值;
|
||||
let 离开斜率 = dy / dx;
|
||||
|
||||
if 进入段.方向() == 相对方向::向上 {
|
||||
@@ -86,12 +92,12 @@ impl 背驰分析 {
|
||||
|
||||
/// 测度背驰 — 价格时间测度背驰
|
||||
pub fn 测度背驰(进入段: &虚线, 离开段: &虚线) -> bool {
|
||||
let dx = (进入段.武.时间戳 - 进入段.文.时间戳) as f64;
|
||||
let dy = 进入段.武.分型特征值 - 进入段.文.分型特征值;
|
||||
let dx = (进入段.武.read().unwrap().时间戳 - 进入段.文.时间戳) as f64;
|
||||
let dy = 进入段.武.read().unwrap().分型特征值 - 进入段.文.分型特征值;
|
||||
let 进入测度 = (dx * dx + dy * dy).sqrt();
|
||||
|
||||
let dx = (离开段.武.时间戳 - 离开段.文.时间戳) as f64;
|
||||
let dy = 离开段.武.分型特征值 - 离开段.文.分型特征值;
|
||||
let dx = (离开段.武.read().unwrap().时间戳 - 离开段.文.时间戳) as f64;
|
||||
let dy = 离开段.武.read().unwrap().分型特征值 - 离开段.文.分型特征值;
|
||||
let 离开测度 = (dx * dx + dy * dy).sqrt();
|
||||
|
||||
if 进入段.方向() == 相对方向::向上 {
|
||||
@@ -102,14 +108,14 @@ impl 背驰分析 {
|
||||
}
|
||||
|
||||
/// 全量背驰 — MACD + 斜率 + 测度 三者全满足
|
||||
pub fn 全量背驰(进入段: &虚线, 离开段: &虚线, 普K序列: &[Rc<K线>]) -> bool {
|
||||
pub fn 全量背驰(进入段: &虚线, 离开段: &虚线, 普K序列: &[Arc<K线>]) -> bool {
|
||||
Self::MACD背驰(进入段, 离开段, 普K序列, "总")
|
||||
&& Self::测度背驰(进入段, 离开段)
|
||||
&& Self::斜率背驰(进入段, 离开段)
|
||||
}
|
||||
|
||||
/// 任意背驰 — 任一条件满足即可
|
||||
pub fn 任意背驰(进入段: &虚线, 离开段: &虚线, 普K序列: &[Rc<K线>]) -> bool {
|
||||
pub fn 任意背驰(进入段: &虚线, 离开段: &虚线, 普K序列: &[Arc<K线>]) -> bool {
|
||||
Self::MACD背驰(进入段, 离开段, 普K序列, "总")
|
||||
|| Self::测度背驰(进入段, 离开段)
|
||||
|| Self::斜率背驰(进入段, 离开段)
|
||||
@@ -119,7 +125,7 @@ impl 背驰分析 {
|
||||
pub fn 配置背驰(
|
||||
进入段: &虚线,
|
||||
离开段: &虚线,
|
||||
普K序列: &[Rc<K线>],
|
||||
普K序列: &[Arc<K线>],
|
||||
配置: &缠论配置,
|
||||
) -> bool {
|
||||
match (
|
||||
@@ -152,7 +158,7 @@ impl 背驰分析 {
|
||||
}
|
||||
|
||||
/// 任选背驰 — 至少两个条件满足(多数投票)
|
||||
pub fn 任选背驰(进入段: &虚线, 离开段: &虚线, 普K序列: &[Rc<K线>]) -> bool {
|
||||
pub fn 任选背驰(进入段: &虚线, 离开段: &虚线, 普K序列: &[Arc<K线>]) -> bool {
|
||||
let 混沌槽 = [
|
||||
Self::MACD背驰(进入段, 离开段, 普K序列, "总"),
|
||||
Self::测度背驰(进入段, 离开段),
|
||||
@@ -165,7 +171,7 @@ impl 背驰分析 {
|
||||
pub fn 背驰模式(
|
||||
进入段: &虚线,
|
||||
离开段: &虚线,
|
||||
普K序列: &[Rc<K线>],
|
||||
普K序列: &[Arc<K线>],
|
||||
配置: &缠论配置,
|
||||
模式: &str,
|
||||
) -> bool {
|
||||
@@ -180,9 +186,13 @@ impl 背驰分析 {
|
||||
|
||||
// ---- 内部辅助 ----
|
||||
|
||||
fn _获取MACD面积(K线序列: &[Rc<K线>], 始: &Rc<K线>, 终: &Rc<K线>) -> MACD面积 {
|
||||
let 始_idx = K线序列.iter().position(|k| Rc::as_ptr(k) == Rc::as_ptr(始));
|
||||
let 终_idx = K线序列.iter().position(|k| Rc::as_ptr(k) == Rc::as_ptr(终));
|
||||
fn _获取MACD面积(K线序列: &[Arc<K线>], 始: &Arc<K线>, 终: &Arc<K线>) -> MACD面积 {
|
||||
let 始_idx = K线序列
|
||||
.iter()
|
||||
.position(|k| Arc::as_ptr(k) == Arc::as_ptr(始));
|
||||
let 终_idx = K线序列
|
||||
.iter()
|
||||
.position(|k| Arc::as_ptr(k) == Arc::as_ptr(终));
|
||||
|
||||
let mut 阳 = 0.0f64;
|
||||
let mut 阴 = 0.0f64;
|
||||
|
||||
+475
-124
@@ -25,35 +25,50 @@
|
||||
use crate::structure::dash_line::虚线;
|
||||
use crate::structure::fractal_obj::分型;
|
||||
use crate::types::相对方向;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
/// 中枢 — 三段虚线重叠区间构成的价格中枢
|
||||
#[derive(Debug, Clone)]
|
||||
/// 可变字段使用 Cell/RefCell 实现内部可变性,确保 Rc 指针身份一致
|
||||
#[derive(Debug)]
|
||||
pub struct 中枢 {
|
||||
pub 序号: i64,
|
||||
pub 标识: String,
|
||||
pub 级别: i64,
|
||||
pub 基础序列: Vec<Rc<虚线>>,
|
||||
pub 第三买卖线: Option<Rc<虚线>>,
|
||||
pub 本级_第三买卖线: Option<Rc<虚线>>,
|
||||
pub 序号: AtomicI64,
|
||||
pub 标识: RwLock<String>,
|
||||
pub 级别: AtomicI64,
|
||||
pub 基础序列: RwLock<Vec<Arc<虚线>>>,
|
||||
pub 第三买卖线: RwLock<Option<Arc<虚线>>>,
|
||||
pub 本级_第三买卖线: RwLock<Option<Arc<虚线>>>,
|
||||
}
|
||||
|
||||
impl Clone for 中枢 {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
序号: AtomicI64::new(self.序号.load(Ordering::Relaxed)),
|
||||
标识: RwLock::new(self.标识.read().unwrap().clone()),
|
||||
级别: AtomicI64::new(self.级别.load(Ordering::Relaxed)),
|
||||
基础序列: RwLock::new(self.基础序列.read().unwrap().clone()),
|
||||
第三买卖线: RwLock::new(self.第三买卖线.read().unwrap().clone()),
|
||||
本级_第三买卖线: RwLock::new(self.本级_第三买卖线.read().unwrap().clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl 中枢 {
|
||||
pub fn new(序号: i64, 标识: String, 级别: i64, 基础序列: Vec<Rc<虚线>>) -> Self {
|
||||
pub fn new(序号: i64, 标识: String, 级别: i64, 基础序列: Vec<Arc<虚线>>) -> Self {
|
||||
Self {
|
||||
序号,
|
||||
标识,
|
||||
级别,
|
||||
基础序列: 基础序列.into_iter().take(3).collect(),
|
||||
第三买卖线: None,
|
||||
本级_第三买卖线: None,
|
||||
序号: AtomicI64::new(序号),
|
||||
标识: RwLock::new(标识),
|
||||
级别: AtomicI64::new(级别),
|
||||
基础序列: RwLock::new(基础序列.into_iter().take(3).collect()),
|
||||
第三买卖线: RwLock::new(None),
|
||||
本级_第三买卖线: RwLock::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn 添加虚线(&mut self, 实线: Rc<虚线>) {
|
||||
self.基础序列.push(实线);
|
||||
self.本级_第三买卖线 = None;
|
||||
self.第三买卖线 = None;
|
||||
pub fn 添加虚线(&self, 实线: Arc<虚线>) {
|
||||
self.基础序列.write().unwrap().push(实线);
|
||||
*self.本级_第三买卖线.write().unwrap() = None;
|
||||
*self.第三买卖线.write().unwrap() = None;
|
||||
}
|
||||
|
||||
pub fn 图表标题(&self) -> String {
|
||||
@@ -61,21 +76,21 @@ impl 中枢 {
|
||||
"{}:{}:{}:{}",
|
||||
self.文().中.标识,
|
||||
self.文().中.周期,
|
||||
self.标识,
|
||||
self.序号
|
||||
self.标识.read().unwrap(),
|
||||
self.序号.load(Ordering::Relaxed)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn 离开段(&self) -> Rc<虚线> {
|
||||
Rc::clone(&self.基础序列[self.基础序列.len() - 1])
|
||||
pub fn 离开段(&self) -> Arc<虚线> {
|
||||
Arc::clone(&self.基础序列.read().unwrap()[self.基础序列.read().unwrap().len() - 1])
|
||||
}
|
||||
|
||||
pub fn 方向(&self) -> 相对方向 {
|
||||
self.基础序列[0].方向().翻转()
|
||||
self.基础序列.read().unwrap()[0].方向().翻转()
|
||||
}
|
||||
|
||||
pub fn 高(&self) -> f64 {
|
||||
self.基础序列[..3]
|
||||
self.基础序列.read().unwrap()[..3]
|
||||
.iter()
|
||||
.map(|x| x.高())
|
||||
.min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
|
||||
@@ -83,7 +98,7 @@ impl 中枢 {
|
||||
}
|
||||
|
||||
pub fn 低(&self) -> f64 {
|
||||
self.基础序列[..3]
|
||||
self.基础序列.read().unwrap()[..3]
|
||||
.iter()
|
||||
.map(|x| x.低())
|
||||
.max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
|
||||
@@ -92,6 +107,8 @@ impl 中枢 {
|
||||
|
||||
pub fn 高高(&self) -> f64 {
|
||||
self.基础序列
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|x| x.高())
|
||||
.max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
|
||||
@@ -100,47 +117,54 @@ impl 中枢 {
|
||||
|
||||
pub fn 低低(&self) -> f64 {
|
||||
self.基础序列
|
||||
.read()
|
||||
.unwrap()
|
||||
.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) -> Arc<分型> {
|
||||
Arc::clone(&self.基础序列.read().unwrap()[0].文)
|
||||
}
|
||||
|
||||
pub fn 武(&self) -> Rc<分型> {
|
||||
Rc::clone(&self.基础序列[self.基础序列.len() - 1].武)
|
||||
pub fn 武(&self) -> Arc<分型> {
|
||||
Arc::clone(
|
||||
&*self.基础序列.read().unwrap()[self.基础序列.read().unwrap().len() - 1]
|
||||
.武
|
||||
.read()
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn 设置第三买卖线(&mut self, 线: Rc<虚线>) {
|
||||
self.第三买卖线 = Some(线);
|
||||
pub fn 设置第三买卖线(&self, 线: Arc<虚线>) {
|
||||
*self.第三买卖线.write().unwrap() = Some(线);
|
||||
}
|
||||
|
||||
/// 获取序列 — 基础序列 + 第三买卖线(若有)
|
||||
pub fn 获取序列(&self) -> Vec<Rc<虚线>> {
|
||||
let mut 序列: Vec<Rc<虚线>> = self.基础序列.clone();
|
||||
if let Some(ref 三买) = self.第三买卖线 {
|
||||
序列.push(Rc::clone(三买));
|
||||
pub fn 获取序列(&self) -> Vec<Arc<虚线>> {
|
||||
let mut 序列: Vec<Arc<虚线>> = self.基础序列.read().unwrap().clone();
|
||||
if let Some(ref 三买) = *self.第三买卖线.read().unwrap() {
|
||||
序列.push(Arc::clone(三买));
|
||||
}
|
||||
序列
|
||||
}
|
||||
|
||||
pub fn 获取数据文本(&self) -> String {
|
||||
let 第三买卖线_str = match &self.第三买卖线 {
|
||||
let 第三买卖线_str = match &*self.第三买卖线.read().unwrap() {
|
||||
Some(x) => format!("{}", x),
|
||||
None => "None".to_string(),
|
||||
};
|
||||
let 本级_第三买卖线_str = match &self.本级_第三买卖线 {
|
||||
let 本级_第三买卖线_str = match &*self.本级_第三买卖线.read().unwrap() {
|
||||
Some(x) => format!("{}", x),
|
||||
None => "None".to_string(),
|
||||
};
|
||||
format!(
|
||||
"{}, {}, {}, 文:({},{}), 武:({},{}), {}, {}",
|
||||
self.标识,
|
||||
self.序号,
|
||||
self.级别,
|
||||
self.标识.read().unwrap(),
|
||||
self.序号.load(Ordering::Relaxed),
|
||||
self.级别.load(Ordering::Relaxed),
|
||||
self.文().时间戳,
|
||||
crate::utils::format_f64_g(self.文().分型特征值),
|
||||
self.武().时间戳,
|
||||
@@ -151,12 +175,12 @@ impl 中枢 {
|
||||
}
|
||||
|
||||
/// 校验中枢合法性
|
||||
pub fn 校验合法性(&mut self, 序列: &[Rc<虚线>]) -> bool {
|
||||
let mut 有效序列 = self.基础序列.clone();
|
||||
let mut 无效序列: Vec<Rc<虚线>> = Vec::new();
|
||||
for 元素 in &self.基础序列 {
|
||||
if !序列.iter().any(|x| Rc::as_ptr(x) == Rc::as_ptr(元素)) {
|
||||
无效序列.push(Rc::clone(元素));
|
||||
pub fn 校验合法性(&self, 序列: &[Arc<虚线>]) -> bool {
|
||||
let mut 有效序列 = self.基础序列.read().unwrap().clone();
|
||||
let mut 无效序列: Vec<Arc<虚线>> = Vec::new();
|
||||
for 元素 in self.基础序列.read().unwrap().iter() {
|
||||
if !序列.iter().any(|x| Arc::as_ptr(x) == Arc::as_ptr(元素)) {
|
||||
无效序列.push(Arc::clone(元素));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,50 +188,52 @@ impl 中枢 {
|
||||
let 无效 = &无效序列[0];
|
||||
if let Some(pos) = self
|
||||
.基础序列
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.position(|x| Rc::as_ptr(x) == Rc::as_ptr(无效))
|
||||
.position(|x| Arc::as_ptr(x) == Arc::as_ptr(无效))
|
||||
{
|
||||
有效序列 = self.基础序列[..pos].to_vec();
|
||||
有效序列 = self.基础序列.read().unwrap()[..pos].to_vec();
|
||||
}
|
||||
}
|
||||
|
||||
if 有效序列.len() < 3 {
|
||||
self.第三买卖线 = None;
|
||||
self.本级_第三买卖线 = None;
|
||||
*self.第三买卖线.write().unwrap() = None;
|
||||
*self.本级_第三买卖线.write().unwrap() = None;
|
||||
return false;
|
||||
}
|
||||
|
||||
self.基础序列 = 有效序列;
|
||||
*self.基础序列.write().unwrap() = 有效序列;
|
||||
|
||||
let 中枢高 = self.高();
|
||||
let 中枢低 = self.低();
|
||||
有效序列 = Vec::new();
|
||||
for 元素 in &self.基础序列 {
|
||||
for 元素 in self.基础序列.read().unwrap().iter() {
|
||||
if crate::types::相对方向::分析(中枢高, 中枢低, 元素.高(), 元素.低()).是否缺口()
|
||||
{
|
||||
break;
|
||||
}
|
||||
有效序列.push(Rc::clone(元素));
|
||||
有效序列.push(Arc::clone(元素));
|
||||
}
|
||||
self.基础序列 = 有效序列;
|
||||
*self.基础序列.write().unwrap() = 有效序列;
|
||||
|
||||
if self.基础序列.len() < 3 {
|
||||
if self.基础序列.read().unwrap().len() < 3 {
|
||||
return false;
|
||||
}
|
||||
|
||||
for i in 1..self.基础序列.len() {
|
||||
let 前 = &self.基础序列[i - 1];
|
||||
let 后 = &self.基础序列[i];
|
||||
for i in 1..self.基础序列.read().unwrap().len() {
|
||||
let 前 = &self.基础序列.read().unwrap()[i - 1];
|
||||
let 后 = &self.基础序列.read().unwrap()[i];
|
||||
if !前.之后是(后) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if !crate::types::相对方向::分析(
|
||||
self.基础序列[0].高(),
|
||||
self.基础序列[0].低(),
|
||||
self.基础序列[2].高(),
|
||||
self.基础序列[2].低(),
|
||||
self.基础序列.read().unwrap()[0].高(),
|
||||
self.基础序列.read().unwrap()[0].低(),
|
||||
self.基础序列.read().unwrap()[2].高(),
|
||||
self.基础序列.read().unwrap()[2].低(),
|
||||
)
|
||||
.是否缺口()
|
||||
{
|
||||
@@ -218,10 +244,11 @@ impl 中枢 {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref 三买线) = self.第三买卖线.clone() {
|
||||
if 序列.iter().any(|x| Rc::as_ptr(x) == Rc::as_ptr(三买线)) {
|
||||
if !self.基础序列.last().unwrap().之后是(三买线) {
|
||||
self.第三买卖线 = None;
|
||||
let 三买线_opt = self.第三买卖线.read().unwrap().clone();
|
||||
if let Some(ref 三买线) = 三买线_opt {
|
||||
if 序列.iter().any(|x| Arc::as_ptr(x) == Arc::as_ptr(三买线)) {
|
||||
if !self.基础序列.read().unwrap().last().unwrap().之后是(三买线) {
|
||||
*self.第三买卖线.write().unwrap() = None;
|
||||
} else if !crate::types::相对方向::分析(
|
||||
self.高(),
|
||||
self.低(),
|
||||
@@ -230,11 +257,11 @@ impl 中枢 {
|
||||
)
|
||||
.是否缺口()
|
||||
{
|
||||
self.添加虚线(Rc::clone(三买线));
|
||||
self.第三买卖线 = None;
|
||||
self.添加虚线(Arc::clone(三买线));
|
||||
*self.第三买卖线.write().unwrap() = None;
|
||||
}
|
||||
} else {
|
||||
self.第三买卖线 = None;
|
||||
*self.第三买卖线.write().unwrap() = None;
|
||||
}
|
||||
}
|
||||
true
|
||||
@@ -243,16 +270,18 @@ impl 中枢 {
|
||||
/// 完整性 — 详见教你炒股票43:有关背驰的补习课
|
||||
/// 不完整时下一个中枢大概率会与当前中枢发生扩展
|
||||
pub fn 完整性(&self, 虚实: &str) -> bool {
|
||||
if self.基础序列[0].标识 == "笔" {
|
||||
return self.第三买卖线.is_some();
|
||||
if *self.基础序列.read().unwrap()[0].标识.read().unwrap() == "笔" {
|
||||
return self.第三买卖线.read().unwrap().is_some();
|
||||
}
|
||||
|
||||
let 线段内部中枢 = if 虚实 == "合" {
|
||||
&self.基础序列.last().unwrap().合_中枢序列
|
||||
let 基础序列_ref = self.基础序列.read().unwrap();
|
||||
let 最后段 = 基础序列_ref.last().unwrap();
|
||||
let 内部中枢_vec = if 虚实 == "合" {
|
||||
最后段.合_中枢序列.read().unwrap()
|
||||
} else {
|
||||
&self.基础序列.last().unwrap().实_中枢序列
|
||||
最后段.实_中枢序列.read().unwrap()
|
||||
};
|
||||
for 内部中枢 in 线段内部中枢 {
|
||||
for 内部中枢 in 内部中枢_vec.iter() {
|
||||
if crate::types::相对方向::分析(
|
||||
self.高(),
|
||||
self.低(),
|
||||
@@ -269,16 +298,19 @@ impl 中枢 {
|
||||
|
||||
/// 获取扩展中枢 — 当基础序列 >= 9 时生成扩展中枢
|
||||
pub fn 获取扩展中枢(
|
||||
&self, 扩展中枢: &mut Vec<Rc<中枢>>, 配置: &crate::config::缠论配置
|
||||
&self,
|
||||
扩展中枢: &mut Vec<Arc<中枢>>,
|
||||
配置: &crate::config::缠论配置,
|
||||
) {
|
||||
if self.基础序列.len() >= 9 {
|
||||
let mut 扩展线段: Vec<Rc<虚线>> = Vec::new();
|
||||
crate::algorithm::segment::线段::扩展分析(&self.基础序列, &mut 扩展线段, 配置);
|
||||
if self.基础序列.read().unwrap().len() >= 9 {
|
||||
let mut 扩展线段: Vec<Arc<虚线>> = Vec::new();
|
||||
let 基础序列_ref = self.基础序列.read().unwrap();
|
||||
crate::algorithm::segment::线段::扩展分析(&基础序列_ref, &mut 扩展线段, 配置);
|
||||
中枢::分析(
|
||||
&扩展线段,
|
||||
扩展中枢,
|
||||
false,
|
||||
&format!("{}_扩展中枢_", self.标识),
|
||||
&format!("{}_扩展中枢_", self.标识.read().unwrap()),
|
||||
0,
|
||||
);
|
||||
}
|
||||
@@ -287,9 +319,15 @@ impl 中枢 {
|
||||
/// 当前状态 — 详见教你炒股票49:利润率最大的操作模式
|
||||
/// 返回当前中枢最后一段所处的位置关系:中枢之中/中枢之上/中枢之下
|
||||
pub fn 当前状态(&self) -> &str {
|
||||
let 最后 = self.基础序列.last().unwrap();
|
||||
let 基础序列_ref = self.基础序列.read().unwrap();
|
||||
let 最后 = Arc::clone(基础序列_ref.last().unwrap());
|
||||
let 尾部 = 最后.获取_武();
|
||||
let 关系 = crate::types::相对方向::分析(self.高(), self.低(), 尾部.中.高, 尾部.中.低);
|
||||
let 关系 = crate::types::相对方向::分析(
|
||||
self.高(),
|
||||
self.低(),
|
||||
尾部.中.高.get(),
|
||||
尾部.中.低.get(),
|
||||
);
|
||||
if 关系 == crate::types::相对方向::向上缺口 {
|
||||
"中枢之上"
|
||||
} else if 关系 == crate::types::相对方向::向下缺口 {
|
||||
@@ -319,11 +357,11 @@ impl 中枢 {
|
||||
|
||||
/// 创建中枢
|
||||
pub fn 创建(
|
||||
左: Rc<虚线>, 中: Rc<虚线>, 右: Rc<虚线>, 级别: i64, 标识: &str
|
||||
左: Arc<虚线>, 中: Arc<虚线>, 右: Arc<虚线>, 级别: i64, 标识: &str
|
||||
) -> Self {
|
||||
Self::new(
|
||||
0,
|
||||
format!("{}中枢<{}>", 标识, 中.标识),
|
||||
format!("{}中枢<{}>", 标识, 中.标识.read().unwrap()),
|
||||
级别,
|
||||
vec![左, 中, 右],
|
||||
)
|
||||
@@ -331,17 +369,17 @@ impl 中枢 {
|
||||
|
||||
/// 从序列中获取中枢
|
||||
pub fn 从序列中获取中枢(
|
||||
虚线序列: &[Rc<虚线>],
|
||||
虚线序列: &[Arc<虚线>],
|
||||
起始方向: 相对方向,
|
||||
标识: &str,
|
||||
) -> Option<Rc<中枢>> {
|
||||
) -> Option<Arc<中枢>> {
|
||||
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(中枢));
|
||||
let 中枢 = Self::创建(Arc::clone(左), Arc::clone(中), Arc::clone(右), 0, 标识);
|
||||
return Some(Arc::new(中枢));
|
||||
}
|
||||
}
|
||||
None
|
||||
@@ -349,19 +387,22 @@ impl 中枢 {
|
||||
|
||||
/// 向中枢序列尾部添加
|
||||
pub fn 向中枢序列尾部添加(
|
||||
中枢序列: &mut Vec<Rc<中枢>>, mut 待添加中枢: Rc<中枢>
|
||||
中枢序列: &mut Vec<Arc<中枢>>, mut 待添加中枢: Arc<中枢>
|
||||
) {
|
||||
if let Some(前一个) = 中枢序列.last() {
|
||||
let 新 = Rc::make_mut(&mut 待添加中枢);
|
||||
新.序号 = 前一个.序号 + 1;
|
||||
待添加中枢
|
||||
.序号
|
||||
.store(前一个.序号.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
|
||||
// Python: assert seq[-1].获取序列()[-1].序号 <= new.获取序列()[-1].序号
|
||||
let 前_seq = 前一个.获取序列();
|
||||
let new_seq = 新.获取序列();
|
||||
let new_seq = 待添加中枢.获取序列();
|
||||
if let (Some(前_last), Some(new_last)) = (前_seq.last(), new_seq.last()) {
|
||||
if 前_last.序号 > new_last.序号 {
|
||||
if 前_last.序号.load(Ordering::Relaxed) > new_last.序号.load(Ordering::Relaxed)
|
||||
{
|
||||
panic!(
|
||||
"向中枢序列尾部添加 序号错误 前last={} > new_last={}",
|
||||
前_last.序号, new_last.序号
|
||||
前_last.序号.load(Ordering::Relaxed),
|
||||
new_last.序号.load(Ordering::Relaxed)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -371,10 +412,10 @@ impl 中枢 {
|
||||
|
||||
/// 从中枢序列尾部弹出
|
||||
pub fn 从中枢序列尾部弹出(
|
||||
中枢序列: &mut Vec<Rc<中枢>>,
|
||||
待弹出: &Rc<中枢>,
|
||||
) -> Option<Rc<中枢>> {
|
||||
if 中枢序列.last().map(|x| Rc::as_ptr(x)) == Some(Rc::as_ptr(待弹出)) {
|
||||
中枢序列: &mut Vec<Arc<中枢>>,
|
||||
待弹出: &Arc<中枢>,
|
||||
) -> Option<Arc<中枢>> {
|
||||
if 中枢序列.last().map(|x| Arc::as_ptr(x)) == Some(Arc::as_ptr(待弹出)) {
|
||||
中枢序列.pop()
|
||||
} else {
|
||||
None
|
||||
@@ -385,8 +426,8 @@ impl 中枢 {
|
||||
///
|
||||
/// 每收到新的虚线序列数据后调用,更新中枢序列
|
||||
pub fn 分析(
|
||||
虚线序列: &[Rc<虚线>],
|
||||
中枢序列: &mut Vec<Rc<中枢>>,
|
||||
虚线序列: &[Arc<虚线>],
|
||||
中枢序列: &mut Vec<Arc<中枢>>,
|
||||
跳过首部: bool,
|
||||
标识: &str,
|
||||
层级: i64,
|
||||
@@ -406,9 +447,9 @@ impl 中枢 {
|
||||
// Python: 序号 = 虚线序列.index(左)
|
||||
let 序号 = 虚线序列
|
||||
.iter()
|
||||
.position(|x| Rc::as_ptr(x) == Rc::as_ptr(左))
|
||||
.position(|x| Arc::as_ptr(x) == Arc::as_ptr(左))
|
||||
.unwrap_or(i - 1);
|
||||
if 跳过首部 && (左.序号 == 0 || 序号 == 0) {
|
||||
if 跳过首部 && (左.序号.load(Ordering::Relaxed) == 0 || 序号 == 0) {
|
||||
continue;
|
||||
}
|
||||
if 序号 >= 2 {
|
||||
@@ -422,11 +463,11 @@ impl 中枢 {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let 新中枢 = Rc::new(Self::创建(
|
||||
Rc::clone(左),
|
||||
Rc::clone(中),
|
||||
Rc::clone(右),
|
||||
中.级别,
|
||||
let 新中枢 = Arc::new(Self::创建(
|
||||
Arc::clone(左),
|
||||
Arc::clone(中),
|
||||
Arc::clone(右),
|
||||
中.级别.load(Ordering::Relaxed),
|
||||
标识,
|
||||
));
|
||||
Self::向中枢序列尾部添加(中枢序列, 新中枢);
|
||||
@@ -441,13 +482,10 @@ impl 中枢 {
|
||||
// 增量更新
|
||||
let mut 当前中枢_idx = 中枢序列.len() - 1;
|
||||
|
||||
// Validate in-place via Rc::make_mut — avoids full中枢 struct clone
|
||||
let needs_pop = {
|
||||
let cur = Rc::make_mut(&mut 中枢序列[当前中枢_idx]);
|
||||
!cur.校验合法性(虚线序列)
|
||||
};
|
||||
// Validate via shared reference (中枢 uses RwLock internally)
|
||||
let needs_pop = !中枢序列[当前中枢_idx].校验合法性(虚线序列);
|
||||
if needs_pop {
|
||||
let 当前中枢 = Rc::clone(&中枢序列[当前中枢_idx]);
|
||||
let 当前中枢 = Arc::clone(&中枢序列[当前中枢_idx]);
|
||||
Self::从中枢序列尾部弹出(中枢序列, &当前中枢);
|
||||
Self::分析(虚线序列, 中枢序列, 跳过首部, 标识, 层级);
|
||||
return;
|
||||
@@ -456,10 +494,10 @@ impl 中枢 {
|
||||
// 找到当前中枢最后一个元素在虚线序列中的位置
|
||||
let 起始索引 = {
|
||||
let cur = &中枢序列[当前中枢_idx];
|
||||
let 最后元素 = &cur.基础序列[cur.基础序列.len() - 1];
|
||||
let 最后元素 = &cur.基础序列.read().unwrap()[cur.基础序列.read().unwrap().len() - 1];
|
||||
match 虚线序列
|
||||
.iter()
|
||||
.position(|x| Rc::as_ptr(x) == Rc::as_ptr(最后元素))
|
||||
.position(|x| Arc::as_ptr(x) == Arc::as_ptr(最后元素))
|
||||
{
|
||||
Some(idx) => idx + 1,
|
||||
None => return,
|
||||
@@ -468,10 +506,10 @@ impl 中枢 {
|
||||
|
||||
let mut 中枢高 = 中枢序列[当前中枢_idx].高();
|
||||
let mut 中枢低 = 中枢序列[当前中枢_idx].低();
|
||||
let mut 候选序列: Vec<Rc<虚线>> = Vec::new();
|
||||
let mut 候选序列: Vec<Arc<虚线>> = Vec::new();
|
||||
|
||||
for i in 起始索引..虚线序列.len() {
|
||||
let 当前虚线 = Rc::clone(&虚线序列[i]);
|
||||
let 当前虚线 = Arc::clone(&虚线序列[i]);
|
||||
|
||||
// 检查是否超出中枢范围(缺口)
|
||||
if crate::types::相对方向::分析(中枢高, 中枢低, 当前虚线.高(), 当前虚线.低()).是否缺口()
|
||||
@@ -481,16 +519,20 @@ impl 中枢 {
|
||||
// Python: if 当前中枢.基础序列[-1].之后是(当前虚线):
|
||||
let needs_三买 = {
|
||||
let cur = &中枢序列[当前中枢_idx];
|
||||
cur.基础序列.last().unwrap().之后是(&当前虚线)
|
||||
cur.基础序列
|
||||
.read()
|
||||
.unwrap()
|
||||
.last()
|
||||
.unwrap()
|
||||
.之后是(&当前虚线)
|
||||
};
|
||||
if needs_三买 {
|
||||
Rc::make_mut(&mut 中枢序列[当前中枢_idx])
|
||||
.设置第三买卖线(当前虚线.clone());
|
||||
中枢序列[当前中枢_idx].设置第三买卖线(当前虚线.clone());
|
||||
}
|
||||
} else {
|
||||
if 候选序列.is_empty() {
|
||||
// 仍在范围内:延伸中枢
|
||||
Rc::make_mut(&mut 中枢序列[当前中枢_idx]).添加虚线(当前虚线);
|
||||
中枢序列[当前中枢_idx].添加虚线(当前虚线);
|
||||
} else {
|
||||
候选序列.push(当前虚线);
|
||||
}
|
||||
@@ -500,6 +542,8 @@ impl 中枢 {
|
||||
while 候选序列.len() >= 3 {
|
||||
let 起始方向 = 中枢序列[当前中枢_idx]
|
||||
.基础序列
|
||||
.read()
|
||||
.unwrap()
|
||||
.last()
|
||||
.unwrap()
|
||||
.方向()
|
||||
@@ -522,10 +566,317 @@ impl 中枢 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::kline::bar::K线;
|
||||
use crate::kline::chan_kline::缠论K线;
|
||||
use crate::structure::fractal_obj::分型;
|
||||
use crate::types::分型结构;
|
||||
|
||||
fn 辅助_创建K线(时间戳: i64, 高: f64, 低: f64, 开: f64, 收: f64) -> K线 {
|
||||
let mut k = K线::default();
|
||||
k.时间戳 = 时间戳;
|
||||
k.高 = 高;
|
||||
k.低 = 低;
|
||||
k.开盘价 = 开;
|
||||
k.收盘价 = 收;
|
||||
k
|
||||
}
|
||||
|
||||
fn 辅助_创建缠K(
|
||||
时间戳: i64,
|
||||
高: f64,
|
||||
低: f64,
|
||||
方向: 相对方向,
|
||||
结构: Option<分型结构>,
|
||||
序号: i64,
|
||||
) -> Arc<缠论K线> {
|
||||
let 普K = Arc::new(辅助_创建K线(时间戳, 高, 低, 低, 高));
|
||||
Arc::new(缠论K线::创建缠K(
|
||||
时间戳, 高, 低, 方向, 结构, 序号, 普K, None,
|
||||
))
|
||||
}
|
||||
|
||||
fn 辅助_创建顶分型(时间戳: i64, 高: f64, 低: f64, 序号: i64) -> Arc<分型> {
|
||||
let 左 = 辅助_创建缠K(
|
||||
时间戳 - 2,
|
||||
高 - 2.0,
|
||||
低 - 2.0,
|
||||
相对方向::向上,
|
||||
Some(分型结构::上),
|
||||
序号 - 2,
|
||||
);
|
||||
let 中 = 辅助_创建缠K(时间戳, 高, 低, 相对方向::向上, Some(分型结构::顶), 序号);
|
||||
let 右 = 辅助_创建缠K(
|
||||
时间戳 + 2,
|
||||
高 - 1.0,
|
||||
低 - 1.0,
|
||||
相对方向::向下,
|
||||
Some(分型结构::下),
|
||||
序号 + 2,
|
||||
);
|
||||
Arc::new(分型::new(Some(左), 中, Some(右)))
|
||||
}
|
||||
|
||||
fn 辅助_创建底分型(时间戳: i64, 高: f64, 低: f64, 序号: i64) -> Arc<分型> {
|
||||
let 左 = 辅助_创建缠K(
|
||||
时间戳 - 2,
|
||||
高 + 2.0,
|
||||
低 + 2.0,
|
||||
相对方向::向下,
|
||||
Some(分型结构::下),
|
||||
序号 - 2,
|
||||
);
|
||||
let 中 = 缠论K线::创建缠K(
|
||||
时间戳,
|
||||
高,
|
||||
低,
|
||||
相对方向::向下,
|
||||
Some(分型结构::底),
|
||||
序号,
|
||||
Arc::new(辅助_创建K线(时间戳, 高, 低, 低, 高)),
|
||||
None,
|
||||
);
|
||||
中.分型特征值.set(低);
|
||||
let 中 = Arc::new(中);
|
||||
let 右 = 辅助_创建缠K(
|
||||
时间戳 + 2,
|
||||
高 + 1.0,
|
||||
低 + 1.0,
|
||||
相对方向::向上,
|
||||
Some(分型结构::上),
|
||||
序号 + 2,
|
||||
);
|
||||
Arc::new(分型::new(Some(左), 中, Some(右)))
|
||||
}
|
||||
|
||||
fn 辅助_创建笔(
|
||||
文时间戳: i64,
|
||||
文高: f64,
|
||||
文低: f64,
|
||||
武时间戳: i64,
|
||||
武高: f64,
|
||||
武低: f64,
|
||||
) -> Arc<虚线> {
|
||||
let 顶 = 辅助_创建顶分型(文时间戳, 文高, 文低, 1);
|
||||
let 底 = 辅助_创建底分型(武时间戳, 武高, 武低, 2);
|
||||
Arc::new(虚线::创建笔(顶, 底, true))
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 中枢 Cell/RefCell 字段读写
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_中枢创建后字段初始值正确() {
|
||||
let 笔1 = 辅助_创建笔(100, 50.0, 40.0, 200, 30.0, 20.0);
|
||||
let 笔2 = 辅助_创建笔(200, 30.0, 20.0, 300, 55.0, 45.0);
|
||||
let 笔3 = 辅助_创建笔(300, 55.0, 45.0, 400, 25.0, 15.0);
|
||||
|
||||
let 中枢 = 中枢::new(
|
||||
1,
|
||||
"测试中枢".into(),
|
||||
1,
|
||||
vec![Arc::clone(&笔1), Arc::clone(&笔2), Arc::clone(&笔3)],
|
||||
);
|
||||
|
||||
assert_eq!(中枢.序号.load(Ordering::Relaxed), 1);
|
||||
assert_eq!(*中枢.标识.read().unwrap(), "测试中枢");
|
||||
assert_eq!(中枢.级别.load(Ordering::Relaxed), 1);
|
||||
assert_eq!(中枢.基础序列.read().unwrap().len(), 3);
|
||||
assert!(中枢.第三买卖线.read().unwrap().is_none());
|
||||
assert!(中枢.本级_第三买卖线.read().unwrap().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_中枢CellRefCell字段读写() {
|
||||
let 笔1 = 辅助_创建笔(100, 50.0, 40.0, 200, 30.0, 20.0);
|
||||
let 笔2 = 辅助_创建笔(200, 30.0, 20.0, 300, 55.0, 45.0);
|
||||
let 笔3 = 辅助_创建笔(300, 55.0, 45.0, 400, 25.0, 15.0);
|
||||
|
||||
let 中枢 = 中枢::new(
|
||||
0,
|
||||
"测试".into(),
|
||||
1,
|
||||
vec![Arc::clone(&笔1), Arc::clone(&笔2), Arc::clone(&笔3)],
|
||||
);
|
||||
|
||||
// Cell 序号读写
|
||||
中枢.序号.store(99, Ordering::Relaxed);
|
||||
assert_eq!(中枢.序号.load(Ordering::Relaxed), 99);
|
||||
|
||||
// RefCell 第三买卖线读写
|
||||
中枢.设置第三买卖线(Arc::clone(&笔1));
|
||||
assert!(中枢.第三买卖线.read().unwrap().is_some());
|
||||
assert_eq!(
|
||||
Arc::as_ptr(&*中枢.第三买卖线.read().unwrap().as_ref().unwrap()),
|
||||
Arc::as_ptr(&笔1)
|
||||
);
|
||||
|
||||
// 本级_第三买卖线
|
||||
assert!(中枢.本级_第三买卖线.read().unwrap().is_none());
|
||||
*中枢.本级_第三买卖线.write().unwrap() = Some(Arc::clone(&笔3));
|
||||
assert!(中枢.本级_第三买卖线.read().unwrap().is_some());
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 中枢 添加虚线
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_中枢添加虚线后基础序列扩展() {
|
||||
let 笔1 = 辅助_创建笔(100, 50.0, 40.0, 200, 30.0, 20.0);
|
||||
let 笔2 = 辅助_创建笔(200, 30.0, 20.0, 300, 55.0, 45.0);
|
||||
let 笔3 = 辅助_创建笔(300, 55.0, 45.0, 400, 25.0, 15.0);
|
||||
let 笔4 = 辅助_创建笔(400, 25.0, 15.0, 500, 60.0, 50.0);
|
||||
|
||||
let 中枢 = 中枢::new(
|
||||
0,
|
||||
"测试".into(),
|
||||
1,
|
||||
vec![Arc::clone(&笔1), Arc::clone(&笔2), Arc::clone(&笔3)],
|
||||
);
|
||||
assert_eq!(中枢.基础序列.read().unwrap().len(), 3);
|
||||
|
||||
中枢.添加虚线(Arc::clone(&笔4));
|
||||
assert_eq!(中枢.基础序列.read().unwrap().len(), 4);
|
||||
assert_eq!(
|
||||
Arc::as_ptr(&中枢.基础序列.read().unwrap()[3]),
|
||||
Arc::as_ptr(&笔4)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_中枢添加虚线后清除第三买卖线() {
|
||||
let 笔1 = 辅助_创建笔(100, 50.0, 40.0, 200, 30.0, 20.0);
|
||||
let 笔2 = 辅助_创建笔(200, 30.0, 20.0, 300, 55.0, 45.0);
|
||||
let 笔3 = 辅助_创建笔(300, 55.0, 45.0, 400, 25.0, 15.0);
|
||||
let 笔4 = 辅助_创建笔(400, 25.0, 15.0, 500, 60.0, 50.0);
|
||||
|
||||
let 中枢 = 中枢::new(
|
||||
0,
|
||||
"测试".into(),
|
||||
1,
|
||||
vec![Arc::clone(&笔1), Arc::clone(&笔2), Arc::clone(&笔3)],
|
||||
);
|
||||
中枢.设置第三买卖线(Arc::clone(&笔1));
|
||||
*中枢.本级_第三买卖线.write().unwrap() = Some(Arc::clone(&笔2));
|
||||
assert!(中枢.第三买卖线.read().unwrap().is_some());
|
||||
assert!(中枢.本级_第三买卖线.read().unwrap().is_some());
|
||||
|
||||
中枢.添加虚线(Arc::clone(&笔4));
|
||||
// 添加虚线后第三买卖线被清除
|
||||
assert!(中枢.第三买卖线.read().unwrap().is_none());
|
||||
assert!(中枢.本级_第三买卖线.read().unwrap().is_none());
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Clone 后 Rc 指针身份一致
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_中枢Clone后基础序列Rc指针一致() {
|
||||
let 笔1 = 辅助_创建笔(100, 50.0, 40.0, 200, 30.0, 20.0);
|
||||
let 笔2 = 辅助_创建笔(200, 30.0, 20.0, 300, 55.0, 45.0);
|
||||
let 笔3 = 辅助_创建笔(300, 55.0, 45.0, 400, 25.0, 15.0);
|
||||
|
||||
let 中枢 = 中枢::new(
|
||||
0,
|
||||
"测试".into(),
|
||||
1,
|
||||
vec![Arc::clone(&笔1), Arc::clone(&笔2), Arc::clone(&笔3)],
|
||||
);
|
||||
中枢.设置第三买卖线(Arc::clone(&笔1));
|
||||
|
||||
let 克隆 = 中枢.clone();
|
||||
|
||||
// 基础序列中的 Rc 指针应一致
|
||||
for i in 0..3 {
|
||||
assert_eq!(
|
||||
Arc::as_ptr(&中枢.基础序列.read().unwrap()[i]),
|
||||
Arc::as_ptr(&克隆.基础序列.read().unwrap()[i])
|
||||
);
|
||||
}
|
||||
|
||||
// 第三买卖线 Rc 指针应一致
|
||||
assert_eq!(
|
||||
Arc::as_ptr(中枢.第三买卖线.read().unwrap().as_ref().unwrap()),
|
||||
Arc::as_ptr(克隆.第三买卖线.read().unwrap().as_ref().unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 中枢 高/低/高高/低低计算
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_中枢高低计算正确() {
|
||||
// 笔1: 顶(高=50,低=45) →底(高=40,低=30) = 向下笔, 高=50, 低=30
|
||||
let 笔1 = 辅助_创建笔(100, 50.0, 45.0, 200, 40.0, 30.0);
|
||||
|
||||
// 笔2: 底(高=40,低=30) →顶(高=55,低=50) = 向上笔, 高=55, 低=30
|
||||
let 底2 = 辅助_创建底分型(200, 40.0, 30.0, 10);
|
||||
let 顶2 = 辅助_创建顶分型(300, 55.0, 50.0, 15);
|
||||
let 笔2 = Arc::new(虚线::创建笔(底2, 顶2, true));
|
||||
|
||||
// 笔3: 顶(高=55,低=50) →底(高=35,低=25) = 向下笔, 高=55, 低=25
|
||||
let 笔3 = 辅助_创建笔(300, 55.0, 50.0, 400, 35.0, 25.0);
|
||||
|
||||
let 中枢 = 中枢::new(
|
||||
0,
|
||||
"测试".into(),
|
||||
1,
|
||||
vec![Arc::clone(&笔1), Arc::clone(&笔2), Arc::clone(&笔3)],
|
||||
);
|
||||
|
||||
// 高 = min(笔1高, 笔2高, 笔3高) = min(50, 55, 55) = 50
|
||||
assert!((中枢.高() - 50.0).abs() < 0.01, "中枢高={}", 中枢.高());
|
||||
// 低 = max(笔1低, 笔2低, 笔3低) = max(30, 30, 25) = 30
|
||||
assert!((中枢.低() - 30.0).abs() < 0.01, "中枢低={}", 中枢.低());
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 多 Rc 共享下修改可见性
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_多Rc共享中枢修改对所有引用可见() {
|
||||
let 笔1 = 辅助_创建笔(100, 50.0, 40.0, 200, 30.0, 20.0);
|
||||
let 笔2 = 辅助_创建笔(200, 30.0, 20.0, 300, 55.0, 45.0);
|
||||
let 笔3 = 辅助_创建笔(300, 55.0, 45.0, 400, 25.0, 15.0);
|
||||
let 笔4 = 辅助_创建笔(400, 25.0, 15.0, 500, 60.0, 50.0);
|
||||
|
||||
let 中枢1 = Arc::new(中枢::new(
|
||||
0,
|
||||
"测试".into(),
|
||||
1,
|
||||
vec![Arc::clone(&笔1), Arc::clone(&笔2), Arc::clone(&笔3)],
|
||||
));
|
||||
let 中枢2 = Arc::clone(&中枢1);
|
||||
|
||||
// 通过 rc1 修改序号
|
||||
中枢1.序号.store(88, Ordering::Relaxed);
|
||||
assert_eq!(中枢2.序号.load(Ordering::Relaxed), 88);
|
||||
|
||||
// 通过 rc1 添加虚线
|
||||
中枢1.添加虚线(Arc::clone(&笔4));
|
||||
assert_eq!(中枢2.基础序列.read().unwrap().len(), 4);
|
||||
|
||||
// 验证共享的 Arc<虚线> 指针一致
|
||||
assert_eq!(
|
||||
Arc::as_ptr(&中枢1.基础序列.read().unwrap()[3]),
|
||||
Arc::as_ptr(&中枢2.基础序列.read().unwrap()[3])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for 中枢 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let 序列_str = self
|
||||
.基础序列
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|d| format!("{}", d))
|
||||
.collect::<Vec<_>>()
|
||||
@@ -533,10 +884,10 @@ impl std::fmt::Display for 中枢 {
|
||||
write!(
|
||||
f,
|
||||
"{}({}, {}, 元素数量: {}, [{}], {} ===>>> {})",
|
||||
self.标识,
|
||||
self.标识.read().unwrap(),
|
||||
crate::utils::format_f64_g(self.高()),
|
||||
crate::utils::format_f64_g(self.低()),
|
||||
self.基础序列.len(),
|
||||
self.基础序列.read().unwrap().len(),
|
||||
序列_str,
|
||||
self.文(),
|
||||
self.武(),
|
||||
|
||||
+594
-379
File diff suppressed because it is too large
Load Diff
+27
-26
@@ -27,18 +27,19 @@ use crate::kline::chan_kline::缠论K线;
|
||||
use crate::structure::fractal_obj::分型;
|
||||
use crate::types::bsp_type::买卖点类型;
|
||||
use crate::types::分型结构;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// 基础买卖点 — 买卖点的基础数据结构
|
||||
#[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 买卖点分型: Arc<分型>,
|
||||
pub 买卖点K线: Arc<缠论K线>,
|
||||
pub 当前K线: Arc<K线>,
|
||||
pub 失效K线: Option<Arc<K线>>,
|
||||
pub 终结K线: Option<Arc<K线>>,
|
||||
pub 破位值: f64,
|
||||
pub 结构: Option<分型结构>,
|
||||
}
|
||||
@@ -46,12 +47,12 @@ pub struct 基础买卖点 {
|
||||
impl 基础买卖点 {
|
||||
pub fn new(
|
||||
类型: 买卖点类型,
|
||||
当前K线: Rc<K线>,
|
||||
买卖点分型: Rc<分型>,
|
||||
当前K线: Arc<K线>,
|
||||
买卖点分型: Arc<分型>,
|
||||
备注: String,
|
||||
中枢破位值: f64,
|
||||
) -> Self {
|
||||
let 买卖点K线 = Rc::clone(&买卖点分型.中);
|
||||
let 买卖点K线 = Arc::clone(&买卖点分型.中);
|
||||
Self {
|
||||
备注,
|
||||
类型,
|
||||
@@ -67,13 +68,13 @@ impl 基础买卖点 {
|
||||
|
||||
/// 偏移 — 当前K线与买卖点K线的序号差
|
||||
pub fn 偏移(&self) -> i64 {
|
||||
self.当前K线.序号 - self.买卖点K线.序号
|
||||
self.当前K线.序号 - self.买卖点K线.序号.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// 失效偏移
|
||||
pub fn 失效偏移(&self) -> i64 {
|
||||
match &self.失效K线 {
|
||||
Some(k) => k.序号 - self.买卖点K线.序号,
|
||||
Some(k) => k.序号 - self.买卖点K线.序号.load(Ordering::Relaxed),
|
||||
None => -1,
|
||||
}
|
||||
}
|
||||
@@ -112,8 +113,8 @@ pub struct 买卖点;
|
||||
|
||||
impl 买卖点 {
|
||||
pub fn 一卖点(
|
||||
买卖点分型: Rc<分型>,
|
||||
当前K线: Rc<K线>,
|
||||
买卖点分型: Arc<分型>,
|
||||
当前K线: Arc<K线>,
|
||||
_标识: &str,
|
||||
备注: String,
|
||||
中枢破位值: f64,
|
||||
@@ -122,8 +123,8 @@ impl 买卖点 {
|
||||
}
|
||||
|
||||
pub fn 一买点(
|
||||
买卖点分型: Rc<分型>,
|
||||
当前K线: Rc<K线>,
|
||||
买卖点分型: Arc<分型>,
|
||||
当前K线: Arc<K线>,
|
||||
_标识: &str,
|
||||
备注: String,
|
||||
中枢破位值: f64,
|
||||
@@ -132,8 +133,8 @@ impl 买卖点 {
|
||||
}
|
||||
|
||||
pub fn 二卖点(
|
||||
买卖点分型: Rc<分型>,
|
||||
当前K线: Rc<K线>,
|
||||
买卖点分型: Arc<分型>,
|
||||
当前K线: Arc<K线>,
|
||||
_标识: &str,
|
||||
备注: String,
|
||||
中枢破位值: f64,
|
||||
@@ -142,8 +143,8 @@ impl 买卖点 {
|
||||
}
|
||||
|
||||
pub fn 二买点(
|
||||
买卖点分型: Rc<分型>,
|
||||
当前K线: Rc<K线>,
|
||||
买卖点分型: Arc<分型>,
|
||||
当前K线: Arc<K线>,
|
||||
_标识: &str,
|
||||
备注: String,
|
||||
中枢破位值: f64,
|
||||
@@ -152,8 +153,8 @@ impl 买卖点 {
|
||||
}
|
||||
|
||||
pub fn 三卖点(
|
||||
买卖点分型: Rc<分型>,
|
||||
当前K线: Rc<K线>,
|
||||
买卖点分型: Arc<分型>,
|
||||
当前K线: Arc<K线>,
|
||||
_标识: &str,
|
||||
备注: String,
|
||||
中枢破位值: f64,
|
||||
@@ -162,8 +163,8 @@ impl 买卖点 {
|
||||
}
|
||||
|
||||
pub fn 三买点(
|
||||
买卖点分型: Rc<分型>,
|
||||
当前K线: Rc<K线>,
|
||||
买卖点分型: Arc<分型>,
|
||||
当前K线: Arc<K线>,
|
||||
_标识: &str,
|
||||
备注: String,
|
||||
中枢破位值: f64,
|
||||
@@ -176,8 +177,8 @@ impl 买卖点 {
|
||||
特征: &str,
|
||||
序号: &str,
|
||||
级别: &str,
|
||||
买卖点分型: Rc<分型>,
|
||||
当前缠K: Rc<缠论K线>,
|
||||
买卖点分型: Arc<分型>,
|
||||
当前缠K: Arc<缠论K线>,
|
||||
) -> 基础买卖点 {
|
||||
let 买卖 = if matches!(买卖点分型.结构, 分型结构::底 | 分型结构::下) {
|
||||
"买"
|
||||
@@ -188,7 +189,7 @@ impl 买卖点 {
|
||||
let 破位值 = 买卖点分型.分型特征值;
|
||||
|
||||
// 当前K线 — 从缠K获取其标的K线
|
||||
let 当前K线 = Rc::clone(&当前缠K.标的K线);
|
||||
let 当前K线 = Arc::clone(&*当前缠K.标的K线.read().unwrap());
|
||||
|
||||
let 类型 = match (序号, 买卖) {
|
||||
("一", "买") => 买卖点类型::一买,
|
||||
|
||||
@@ -26,9 +26,9 @@ use crate::business::observer::观察者;
|
||||
use crate::business::synthesizer::K线合成器;
|
||||
use crate::config::缠论配置;
|
||||
use crate::kline::bar::K线;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
/// 立体分析器 — 多周期协调器
|
||||
///
|
||||
@@ -38,7 +38,7 @@ pub struct 立体分析器 {
|
||||
pub 周期组: Vec<i64>,
|
||||
输入周期: i64,
|
||||
K线合成器: K线合成器,
|
||||
单体分析器: HashMap<i64, Rc<RefCell<观察者>>>,
|
||||
单体分析器: HashMap<i64, Arc<RwLock<观察者>>>,
|
||||
}
|
||||
|
||||
impl 立体分析器 {
|
||||
@@ -96,13 +96,13 @@ impl 立体分析器 {
|
||||
// Dispatch on completion events (matching Python's __K线回调)
|
||||
for (周期, 完成K线) in 完成事件 {
|
||||
if let Some(观察员) = self.单体分析器.get(&周期) {
|
||||
观察员.borrow_mut().增加原始K线(完成K线);
|
||||
观察员.write().unwrap().增加原始K线(完成K线);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取指定周期的观察者
|
||||
pub fn 获取观察者(&self, 周期: i64) -> Option<Rc<RefCell<观察者>>> {
|
||||
pub fn 获取观察者(&self, 周期: i64) -> Option<Arc<RwLock<观察者>>> {
|
||||
self.单体分析器.get(&周期).cloned()
|
||||
}
|
||||
|
||||
@@ -116,23 +116,23 @@ impl 立体分析器 {
|
||||
let 起始时间 = self
|
||||
.单体分析器
|
||||
.get(&self.输入周期)
|
||||
.and_then(|o| o.borrow().普通K线序列.first().map(|k| k.时间戳))
|
||||
.and_then(|o| o.read().unwrap().普通K线序列.first().map(|k| k.时间戳))
|
||||
.unwrap_or(0);
|
||||
let 结束时间 = self
|
||||
.单体分析器
|
||||
.get(&self.输入周期)
|
||||
.and_then(|o| o.borrow().普通K线序列.last().map(|k| k.时间戳))
|
||||
.and_then(|o| o.read().unwrap().普通K线序列.last().map(|k| k.时间戳))
|
||||
.unwrap_or(0);
|
||||
let 标识 = self
|
||||
.单体分析器
|
||||
.get(&self.输入周期)
|
||||
.map(|o| o.borrow().符号.clone())
|
||||
.map(|o| o.read().unwrap().符号.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
let 周期 = self
|
||||
.单体分析器
|
||||
.get(&self.输入周期)
|
||||
.map(|o| o.borrow().周期)
|
||||
.map(|o| o.read().unwrap().周期)
|
||||
.unwrap_or_default();
|
||||
|
||||
let 目录标识 = format!("RustM_{}:{}_{}_{}", 标识, 周期, 起始时间, 结束时间);
|
||||
@@ -146,7 +146,8 @@ impl 立体分析器 {
|
||||
for 周期 in &self.周期组 {
|
||||
if let Some(观察员) = self.单体分析器.get(周期) {
|
||||
观察员
|
||||
.borrow()
|
||||
.read()
|
||||
.unwrap()
|
||||
.测试_保存数据(Some(&保存路径.to_string_lossy()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ use crate::structure::dash_line::虚线;
|
||||
use crate::structure::fractal_obj::分型;
|
||||
use crate::types::相对方向;
|
||||
use crate::utils::datetime;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
/// 观察者 — 单周期分析器,持有所有层级序列,接收K线流式输入后逐层计算
|
||||
pub struct 观察者 {
|
||||
@@ -42,40 +42,41 @@ pub struct 观察者 {
|
||||
pub 配置: 缠论配置,
|
||||
|
||||
// K线序列
|
||||
pub 普通K线序列: Vec<Rc<K线>>,
|
||||
pub 缠论K线序列: Vec<Rc<缠论K线>>,
|
||||
pub 普通K线序列: Vec<Arc<K线>>,
|
||||
pub 基础缠K序列: Vec<Arc<缠论K线>>,
|
||||
pub 缠论K线序列: Vec<Arc<缠论K线>>,
|
||||
|
||||
// 分型与笔
|
||||
pub 分型序列: Vec<Rc<分型>>,
|
||||
pub 笔序列: Vec<Rc<虚线>>,
|
||||
pub 笔_中枢序列: Vec<Rc<中枢>>,
|
||||
pub 分型序列: Vec<Arc<分型>>,
|
||||
pub 笔序列: Vec<Arc<虚线>>,
|
||||
pub 笔_中枢序列: Vec<Arc<中枢>>,
|
||||
|
||||
// 线段
|
||||
pub 线段序列: Vec<Rc<虚线>>,
|
||||
pub 中枢序列: Vec<Rc<中枢>>,
|
||||
pub 线段序列: Vec<Arc<虚线>>,
|
||||
pub 中枢序列: Vec<Arc<中枢>>,
|
||||
|
||||
// 扩展线段(笔级)
|
||||
pub 扩展线段序列: Vec<Rc<虚线>>,
|
||||
pub 扩展中枢序列: Vec<Rc<中枢>>,
|
||||
pub 扩展线段序列: Vec<Arc<虚线>>,
|
||||
pub 扩展中枢序列: Vec<Arc<中枢>>,
|
||||
|
||||
// 扩展线段(线段级)
|
||||
pub 扩展线段序列_线段: Vec<Rc<虚线>>,
|
||||
pub 扩展中枢序列_线段: Vec<Rc<中枢>>,
|
||||
pub 扩展线段序列_线段: Vec<Arc<虚线>>,
|
||||
pub 扩展中枢序列_线段: Vec<Arc<中枢>>,
|
||||
|
||||
// 线段之线段
|
||||
pub 线段_线段序列: Vec<Rc<虚线>>,
|
||||
pub 线段_中枢序列: Vec<Rc<中枢>>,
|
||||
pub 线段_线段序列: Vec<Arc<虚线>>,
|
||||
pub 线段_中枢序列: Vec<Arc<中枢>>,
|
||||
|
||||
// 扩展线段之扩展线段
|
||||
pub 扩展线段序列_扩展线段: Vec<Rc<虚线>>,
|
||||
pub 扩展中枢序列_扩展线段: Vec<Rc<中枢>>,
|
||||
pub 扩展线段序列_扩展线段: Vec<Arc<虚线>>,
|
||||
pub 扩展中枢序列_扩展线段: Vec<Arc<中枢>>,
|
||||
|
||||
// 终止时间戳
|
||||
终止时间戳: Option<i64>,
|
||||
}
|
||||
|
||||
impl 观察者 {
|
||||
pub fn new(符号: String, 周期: i64, 配置: 缠论配置) -> Rc<RefCell<Self>> {
|
||||
pub fn new(符号: String, 周期: i64, 配置: 缠论配置) -> Arc<RwLock<Self>> {
|
||||
let 终止时间戳 = if 配置.手动终止 != "1970-01-01 00:00:00" && !配置.手动终止.is_empty()
|
||||
{
|
||||
datetime::转化为时间戳(&配置.手动终止)
|
||||
@@ -88,6 +89,7 @@ impl 观察者 {
|
||||
周期,
|
||||
配置,
|
||||
普通K线序列: Vec::new(),
|
||||
基础缠K序列: Vec::new(),
|
||||
缠论K线序列: Vec::new(),
|
||||
分型序列: Vec::new(),
|
||||
笔序列: Vec::new(),
|
||||
@@ -105,7 +107,7 @@ impl 观察者 {
|
||||
终止时间戳,
|
||||
};
|
||||
instance.配置.标识 = 符号;
|
||||
Rc::new(RefCell::new(instance))
|
||||
Arc::new(RwLock::new(instance))
|
||||
}
|
||||
|
||||
/// 标识
|
||||
@@ -114,18 +116,19 @@ impl 观察者 {
|
||||
}
|
||||
|
||||
/// 当前K线
|
||||
pub fn 当前K线(&self) -> Option<&Rc<K线>> {
|
||||
pub fn 当前K线(&self) -> Option<&Arc<K线>> {
|
||||
self.普通K线序列.last()
|
||||
}
|
||||
|
||||
/// 当前缠K
|
||||
pub fn 当前缠K(&self) -> Option<&Rc<缠论K线>> {
|
||||
pub fn 当前缠K(&self) -> Option<&Arc<缠论K线>> {
|
||||
self.缠论K线序列.last()
|
||||
}
|
||||
|
||||
/// 重置基础序列
|
||||
pub fn 重置基础序列(&mut self) {
|
||||
self.普通K线序列.clear();
|
||||
self.基础缠K序列.clear();
|
||||
self.缠论K线序列.clear();
|
||||
self.分型序列.clear();
|
||||
self.笔序列.clear();
|
||||
@@ -232,13 +235,7 @@ impl 观察者 {
|
||||
&mut self.线段_线段序列,
|
||||
&self.配置,
|
||||
0,
|
||||
&[
|
||||
相对方向::向下,
|
||||
相对方向::向上,
|
||||
相对方向::顺,
|
||||
相对方向::逆,
|
||||
相对方向::同,
|
||||
],
|
||||
&[相对方向::向上, 相对方向::向下],
|
||||
);
|
||||
}
|
||||
if self.配置.分析线段中枢 {
|
||||
@@ -282,12 +279,12 @@ impl 观察者 {
|
||||
|
||||
for i in 1..self.缠论K线序列.len() - 1 {
|
||||
let 当前分型 = 分型::new(
|
||||
Some(Rc::clone(&self.缠论K线序列[i - 1])),
|
||||
Rc::clone(&self.缠论K线序列[i]),
|
||||
Some(Rc::clone(&self.缠论K线序列[i + 1])),
|
||||
Some(Arc::clone(&self.缠论K线序列[i - 1])),
|
||||
Arc::clone(&self.缠论K线序列[i]),
|
||||
Some(Arc::clone(&self.缠论K线序列[i + 1])),
|
||||
);
|
||||
笔::分析(
|
||||
Rc::new(当前分型),
|
||||
Arc::new(当前分型),
|
||||
&mut self.分型序列,
|
||||
&mut self.笔序列,
|
||||
&self.缠论K线序列,
|
||||
@@ -339,13 +336,7 @@ impl 观察者 {
|
||||
&mut self.线段_线段序列,
|
||||
&self.配置,
|
||||
0,
|
||||
&[
|
||||
相对方向::向下,
|
||||
相对方向::向上,
|
||||
相对方向::顺,
|
||||
相对方向::逆,
|
||||
相对方向::同,
|
||||
],
|
||||
&[相对方向::向上, 相对方向::向下],
|
||||
);
|
||||
}
|
||||
if self.配置.分析线段中枢 {
|
||||
@@ -424,14 +415,14 @@ impl 观察者 {
|
||||
.map(|ck| {
|
||||
format!(
|
||||
"缠K, {}, {}, {:?}, {}, {}, {}, {}, {}",
|
||||
ck.序号,
|
||||
ck.时间戳,
|
||||
ck.序号.load(Ordering::Relaxed),
|
||||
ck.时间戳.load(Ordering::Relaxed),
|
||||
ck.分型,
|
||||
ck.方向,
|
||||
ck.高,
|
||||
ck.低,
|
||||
*ck.方向.read().unwrap(),
|
||||
ck.高.get(),
|
||||
ck.低.get(),
|
||||
ck.原始起始序号,
|
||||
ck.原始结束序号
|
||||
ck.原始结束序号.load(Ordering::Relaxed)
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
@@ -442,7 +433,12 @@ impl 观察者 {
|
||||
.map(|(i, fx)| {
|
||||
format!(
|
||||
"分型, {}, {}, {:?}, {}, {}, {}",
|
||||
i, fx.时间戳, fx.结构, fx.分型特征值, fx.中.时间戳, fx.中.低,
|
||||
i,
|
||||
fx.时间戳,
|
||||
fx.结构,
|
||||
fx.分型特征值,
|
||||
fx.中.时间戳.load(Ordering::Relaxed),
|
||||
fx.中.低.get(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
@@ -510,7 +506,7 @@ impl 观察者 {
|
||||
pub fn 读取数据文件(
|
||||
文件路径: &str,
|
||||
配置: Option<缠论配置>,
|
||||
) -> Result<Rc<RefCell<Self>>, String> {
|
||||
) -> Result<Arc<RwLock<Self>>, String> {
|
||||
let 配置 = 配置.unwrap_or_default();
|
||||
|
||||
// Parse filename: btcusd-300-1631772074-1632222374.nb
|
||||
@@ -535,7 +531,7 @@ impl 观察者 {
|
||||
for i in 0..data.len() / size {
|
||||
let offset = i * size;
|
||||
if let Some(k线) = K线::from_bytes(&data[offset..offset + size], 周期, "nb") {
|
||||
实例.borrow_mut().增加原始K线(k线);
|
||||
实例.write().unwrap().增加原始K线(k线);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,37 +544,42 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::config::缠论配置;
|
||||
|
||||
const TEST_DATA_PATH: &str = "/home/moscow/chanlun.rs/btcusd-300-1777649100-1778398800.nb";
|
||||
|
||||
#[test]
|
||||
fn test_普k序列指针一致性() {
|
||||
let config = 缠论配置::default();
|
||||
let obs = 观察者::读取数据文件(
|
||||
"/home/moscow/chanlun.rs/btcusd-300-1777649100-1778398800.nb",
|
||||
Some(config),
|
||||
)
|
||||
.unwrap();
|
||||
let obs_ref = obs.borrow();
|
||||
let obs = 观察者::读取数据文件(TEST_DATA_PATH, Some(config)).unwrap();
|
||||
let obs_ref = obs.read().unwrap();
|
||||
|
||||
// 1. Check that each 笔's 获取普K序列 returns K lines whose Rc pointers
|
||||
// match entries in 普通K线序列
|
||||
for (i, bi) in obs_ref.笔序列.iter().enumerate() {
|
||||
let pu_seq = bi.获取普K序列(&obs_ref.普通K线序列);
|
||||
if pu_seq.is_empty() {
|
||||
println!("笔 {}: 获取普K序列 返回空 (fallback failed)", i);
|
||||
println!(" 文.中.标的K线 原始起始序号: {}", bi.文.中.原始起始序号);
|
||||
println!(" 武.中.标的K线 原始结束序号: {}", bi.武.中.原始结束序号);
|
||||
println!(
|
||||
" 武.中.标的K线 原始结束序号: {}",
|
||||
bi.武
|
||||
.read()
|
||||
.unwrap()
|
||||
.中
|
||||
.原始结束序号
|
||||
.load(Ordering::Relaxed)
|
||||
);
|
||||
println!(" 普通K线序列.len: {}", obs_ref.普通K线序列.len());
|
||||
} else {
|
||||
// Check first element's pointer
|
||||
let first_ptr = Rc::as_ptr(&pu_seq[0]);
|
||||
let first_ptr = Arc::as_ptr(&pu_seq[0]);
|
||||
let found = obs_ref
|
||||
.普通K线序列
|
||||
.iter()
|
||||
.any(|k| Rc::as_ptr(k) == first_ptr);
|
||||
.any(|k| Arc::as_ptr(k) == first_ptr);
|
||||
if !found {
|
||||
println!("笔 {}: 获取普K序列[0] 的 Rc 指针不在 普通K线序列 中!", i);
|
||||
// Check if 文.中.标的K线 pointer is in 普通K线序列
|
||||
let wen_ptr = Rc::as_ptr(&bi.文.中.标的K线);
|
||||
let wen_found = obs_ref.普通K线序列.iter().any(|k| Rc::as_ptr(k) == wen_ptr);
|
||||
let wen_ptr = Arc::as_ptr(&*bi.文.中.标的K线.read().unwrap());
|
||||
let wen_found = obs_ref
|
||||
.普通K线序列
|
||||
.iter()
|
||||
.any(|k| Arc::as_ptr(k) == wen_ptr);
|
||||
println!(" 文.中.标的K线 在序列中: {}", wen_found);
|
||||
} else {
|
||||
println!("笔 {}: OK, 获取普K序列[0] 在序列中找到", i);
|
||||
@@ -589,49 +590,476 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_pyo3_flow_pointer_consistency() {
|
||||
// Simulate what the PyO3 读取数据文件 classmethod does:
|
||||
// 1. Parse K lines from file
|
||||
// 2. Create "K线Py { inner: Rc::new(k线) }" for each
|
||||
// 3. Call observer.增加原始K线(k线_value)
|
||||
|
||||
let config = 缠论配置::default();
|
||||
let obs_ref = 观察者::new("btcusd".into(), 300, config);
|
||||
|
||||
let file_path = "/home/moscow/chanlun.rs/btcusd-300-1777649100-1778398800.nb";
|
||||
let data = std::fs::read(file_path).unwrap();
|
||||
let data = std::fs::read(TEST_DATA_PATH).unwrap();
|
||||
let size = 48;
|
||||
|
||||
for i in 0..data.len() / size {
|
||||
let offset = i * size;
|
||||
if let Some(k线) = K线::from_bytes(&data[offset..offset + size], 300, "btcusd") {
|
||||
// Simulate: K线Py { inner: Rc::new(k线) }
|
||||
let _k线_py_inner = Rc::new(k线.clone());
|
||||
// In the actual PyO3 path, (*普K.borrow().inner).clone() extracts K线 value
|
||||
// which then gets Rc::wrapped inside the observer
|
||||
obs_ref.borrow_mut().增加原始K线(k线);
|
||||
let _k线_py_inner = Arc::new(k线.clone());
|
||||
obs_ref.write().unwrap().增加原始K线(k线);
|
||||
}
|
||||
}
|
||||
|
||||
let obs = obs_ref.borrow();
|
||||
let obs = obs_ref.read().unwrap();
|
||||
println!("普通K线序列.len: {}", obs.普通K线序列.len());
|
||||
println!("笔序列.len: {}", obs.笔序列.len());
|
||||
|
||||
// Now check: does 获取普K序列 return K lines whose pointers match 普通K线序列?
|
||||
for (i, bi) in obs.笔序列.iter().enumerate() {
|
||||
let pu_seq = bi.获取普K序列(&obs.普通K线序列);
|
||||
if pu_seq.is_empty() {
|
||||
println!("笔 {}: 获取普K序列 返回空!", i);
|
||||
} else {
|
||||
let first_ptr = Rc::as_ptr(&pu_seq[0]);
|
||||
let found = obs.普通K线序列.iter().any(|k| Rc::as_ptr(k) == first_ptr);
|
||||
let first_ptr = Arc::as_ptr(&pu_seq[0]);
|
||||
let found = obs.普通K线序列.iter().any(|k| Arc::as_ptr(k) == first_ptr);
|
||||
if !found {
|
||||
println!("笔 {}: 获取普K序列[0] 指针不在序列中!", i);
|
||||
}
|
||||
// Only print first few
|
||||
if i < 5 {
|
||||
println!("笔 {}: OK, len={}", i, pu_seq.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 分型到笔的 Rc 指针一致性
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_分型到笔的文武Rc指针一致性() {
|
||||
let config = 缠论配置::default();
|
||||
let obs = 观察者::读取数据文件(TEST_DATA_PATH, Some(config)).unwrap();
|
||||
let obs_ref = obs.read().unwrap();
|
||||
|
||||
// 每个笔的文/武 分型 Rc 指针必须在 分型序列 中
|
||||
for (i, bi) in obs_ref.笔序列.iter().enumerate() {
|
||||
let 文_ptr = Arc::as_ptr(&bi.文);
|
||||
let 文_found = obs_ref.分型序列.iter().any(|f| Arc::as_ptr(f) == 文_ptr);
|
||||
if !文_found {
|
||||
println!("笔 {}: 文(时间戳={}) 不在分型序列中!", i, bi.文.时间戳);
|
||||
}
|
||||
|
||||
let 武_ptr = Arc::as_ptr(&*bi.武.read().unwrap());
|
||||
let 武_found = obs_ref.分型序列.iter().any(|f| Arc::as_ptr(f) == 武_ptr);
|
||||
if !武_found {
|
||||
println!(
|
||||
"笔 {}: 武(时间戳={}) 不在分型序列中!",
|
||||
i,
|
||||
bi.武.read().unwrap().时间戳
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 笔到线段的 Rc 指针一致性
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_笔到线段的基础序列Rc指针一致性() {
|
||||
let config = 缠论配置::default();
|
||||
let obs = 观察者::读取数据文件(TEST_DATA_PATH, Some(config)).unwrap();
|
||||
let obs_ref = obs.read().unwrap();
|
||||
|
||||
// 每个线段的基础序列中的笔 Rc 指针必须在 笔序列 中
|
||||
for (i, seg) in obs_ref.线段序列.iter().enumerate() {
|
||||
for (j, bi_in_seg) in seg.基础序列.read().unwrap().iter().enumerate() {
|
||||
let bi_ptr = Arc::as_ptr(bi_in_seg);
|
||||
let found = obs_ref.笔序列.iter().any(|b| Arc::as_ptr(b) == bi_ptr);
|
||||
if !found {
|
||||
println!("线段 {} 的基础序列[{}] 不在笔序列中!", i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 中枢基础序列与源的 Rc 指针一致性
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_中枢基础序列与笔序列Rc指针一致() {
|
||||
let config = 缠论配置::default();
|
||||
let obs = 观察者::读取数据文件(TEST_DATA_PATH, Some(config)).unwrap();
|
||||
let obs_ref = obs.read().unwrap();
|
||||
|
||||
for (i, hub) in obs_ref.笔_中枢序列.iter().enumerate() {
|
||||
for (j, bi_in_hub) in hub.基础序列.read().unwrap().iter().enumerate() {
|
||||
let bi_ptr = Arc::as_ptr(bi_in_hub);
|
||||
let found = obs_ref.笔序列.iter().any(|b| Arc::as_ptr(b) == bi_ptr);
|
||||
if !found {
|
||||
println!("笔中枢 {} 的基础序列[{}] 不在笔序列中!", i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i, hub) in obs_ref.中枢序列.iter().enumerate() {
|
||||
for (j, seg_in_hub) in hub.基础序列.read().unwrap().iter().enumerate() {
|
||||
let seg_ptr = Arc::as_ptr(seg_in_hub);
|
||||
let found = obs_ref.线段序列.iter().any(|s| Arc::as_ptr(s) == seg_ptr);
|
||||
if !found {
|
||||
println!("线段中枢 {} 的基础序列[{}] 不在线段序列中!", i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 重复计算一致性
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_重复计算后结果一致() {
|
||||
let data = std::fs::read(TEST_DATA_PATH).unwrap();
|
||||
let size = 48;
|
||||
|
||||
let 计算 = || {
|
||||
let config = 缠论配置::default();
|
||||
let obs_ref = 观察者::new("btcusd".into(), 300, config);
|
||||
for i in 0..data.len() / size {
|
||||
let offset = i * size;
|
||||
if let Some(k线) = K线::from_bytes(&data[offset..offset + size], 300, "btcusd") {
|
||||
obs_ref.write().unwrap().增加原始K线(k线);
|
||||
}
|
||||
}
|
||||
let obs = obs_ref.read().unwrap();
|
||||
(
|
||||
obs.笔序列.len(),
|
||||
obs.线段序列.len(),
|
||||
obs.中枢序列.len(),
|
||||
obs.笔_中枢序列.len(),
|
||||
)
|
||||
};
|
||||
|
||||
let (笔数1, 段数1, 中枢1, 笔中枢1) = 计算();
|
||||
let (笔数2, 段数2, 中枢2, 笔中枢2) = 计算();
|
||||
|
||||
assert_eq!(笔数1, 笔数2, "重复计算笔数不一致");
|
||||
assert_eq!(段数1, 段数2, "重复计算线段数不一致");
|
||||
assert_eq!(中枢1, 中枢2, "重复计算中枢数不一致");
|
||||
assert_eq!(笔中枢1, 笔中枢2, "重复计算笔中枢数不一致");
|
||||
|
||||
println!(
|
||||
"两次计算结果一致: 笔={}, 线段={}, 中枢={}, 笔中枢={}",
|
||||
笔数1, 段数1, 中枢1, 笔中枢1
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 重置后重新投喂数据一致性
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_重置后重新投喂数据一致() {
|
||||
let config = 缠论配置::default();
|
||||
let obs_ref = 观察者::new("btcusd".into(), 300, config);
|
||||
|
||||
let data = std::fs::read(TEST_DATA_PATH).unwrap();
|
||||
let size = 48;
|
||||
|
||||
for i in 0..data.len() / size {
|
||||
let offset = i * size;
|
||||
if let Some(k线) = K线::from_bytes(&data[offset..offset + size], 300, "btcusd") {
|
||||
obs_ref.write().unwrap().增加原始K线(k线);
|
||||
}
|
||||
}
|
||||
|
||||
let 第一次笔数 = obs_ref.read().unwrap().笔序列.len();
|
||||
let 第一次段数 = obs_ref.read().unwrap().线段序列.len();
|
||||
|
||||
// 重置
|
||||
obs_ref.write().unwrap().重置基础序列();
|
||||
assert_eq!(obs_ref.read().unwrap().笔序列.len(), 0);
|
||||
assert_eq!(obs_ref.read().unwrap().线段序列.len(), 0);
|
||||
|
||||
// 重新投喂
|
||||
for i in 0..data.len() / size {
|
||||
let offset = i * size;
|
||||
if let Some(k线) = K线::from_bytes(&data[offset..offset + size], 300, "btcusd") {
|
||||
obs_ref.write().unwrap().增加原始K线(k线);
|
||||
}
|
||||
}
|
||||
|
||||
let 第二次笔数 = obs_ref.read().unwrap().笔序列.len();
|
||||
let 第二次段数 = obs_ref.read().unwrap().线段序列.len();
|
||||
|
||||
assert_eq!(第一次笔数, 第二次笔数, "重置后重新投喂笔数不一致");
|
||||
assert_eq!(第一次段数, 第二次段数, "重置后重新投喂线段数不一致");
|
||||
println!("重置后重投一致: 笔={}, 线段={}", 第一次笔数, 第一次段数);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// RefCell 借用安全性 — 连续大量操作不应 panic
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_RefCell借用安全性_连续读取不panic() {
|
||||
let config = 缠论配置::default();
|
||||
let obs = 观察者::读取数据文件(TEST_DATA_PATH, Some(config)).unwrap();
|
||||
let obs_ref = obs.read().unwrap();
|
||||
|
||||
// 连续大量读取所有 RefCell 字段,不应 panic
|
||||
for _ in 0..100 {
|
||||
for bi in &obs_ref.笔序列 {
|
||||
let _标识 = bi.标识.read().unwrap().clone();
|
||||
let _wu = bi.武.read().unwrap().clone();
|
||||
let _基础序列 = bi.基础序列.read().unwrap().len();
|
||||
let _特征序列 = bi.特征序列.read().unwrap().len();
|
||||
let _模式 = bi.模式.read().unwrap().clone();
|
||||
let _实中枢 = bi.实_中枢序列.read().unwrap().len();
|
||||
let _虚中枢 = bi.虚_中枢序列.read().unwrap().len();
|
||||
let _合中枢 = bi.合_中枢序列.read().unwrap().len();
|
||||
let _确认K = bi.确认K线.read().unwrap().is_some();
|
||||
let _序号 = bi.序号.load(Ordering::Relaxed);
|
||||
let _有效性 = bi.有效性.load(Ordering::Relaxed);
|
||||
let _短路 = bi.短路修正.load(Ordering::Relaxed);
|
||||
let _前一缺口 = *bi.前一缺口.read().unwrap();
|
||||
}
|
||||
for seg in &obs_ref.线段序列 {
|
||||
let _ = seg.标识.read().unwrap().clone();
|
||||
let _ = seg.基础序列.read().unwrap().len();
|
||||
}
|
||||
}
|
||||
// 到达这里 = 无 panic
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_RefCell借用安全性_交替读写不panic() {
|
||||
let config = 缠论配置::default();
|
||||
let obs = 观察者::读取数据文件(TEST_DATA_PATH, Some(config)).unwrap();
|
||||
let obs_ref = obs.read().unwrap();
|
||||
|
||||
// 交替读写 RefCell 字段 — 先读再写同字段,分离 borrow 作用域
|
||||
if !obs_ref.笔序列.is_empty() {
|
||||
let bi = &obs_ref.笔序列[0];
|
||||
// 读
|
||||
let old_mode = bi.模式.read().unwrap().clone();
|
||||
// Ref 已释放,可以写
|
||||
*bi.模式.write().unwrap() = "测试模式".into();
|
||||
let new_mode = bi.模式.read().unwrap().clone();
|
||||
assert_eq!(new_mode, "测试模式");
|
||||
// 恢复
|
||||
*bi.模式.write().unwrap() = old_mode;
|
||||
|
||||
// 读武
|
||||
let old_wu = bi.武.read().unwrap().clone();
|
||||
// Ref 已释放,可以检查
|
||||
assert!(Arc::as_ptr(&old_wu) == Arc::as_ptr(&old_wu));
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 缠K 到 分型 的 Rc 指针一致性
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_缠K到分型的Rc指针一致性() {
|
||||
let config = 缠论配置::default();
|
||||
let obs = 观察者::读取数据文件(TEST_DATA_PATH, Some(config)).unwrap();
|
||||
let obs_ref = obs.read().unwrap();
|
||||
|
||||
// 每个分型的左/中/右 缠K 指针必须在 缠论K线序列 中
|
||||
for (i, f) in obs_ref.分型序列.iter().enumerate() {
|
||||
let 中_ptr = Arc::as_ptr(&f.中);
|
||||
let 中_found = obs_ref.缠论K线序列.iter().any(|k| Arc::as_ptr(k) == 中_ptr);
|
||||
if !中_found {
|
||||
println!("分型 {} 的 中(时间戳={}) 不在缠论K线序列中!", i, f.时间戳);
|
||||
}
|
||||
|
||||
if let Some(ref 左) = f.左 {
|
||||
let 左_ptr = Arc::as_ptr(左);
|
||||
let 左_found = obs_ref.缠论K线序列.iter().any(|k| Arc::as_ptr(k) == 左_ptr);
|
||||
if !左_found {
|
||||
println!("分型 {} 的 左 不在缠论K线序列中!", i);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref 右) = f.右 {
|
||||
let 右_ptr = Arc::as_ptr(右);
|
||||
let 右_found = obs_ref.缠论K线序列.iter().any(|k| Arc::as_ptr(k) == 右_ptr);
|
||||
if !右_found {
|
||||
println!("分型 {} 的 右 不在缠论K线序列中!", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 跨线程安全测试 ==========
|
||||
|
||||
// 编译期断言:核心类型必须实现 Send + Sync
|
||||
#[allow(dead_code)]
|
||||
fn 断言_Send_Sync_编译期检查() {
|
||||
fn _需要_Send<T: Send>() {}
|
||||
fn _需要_Sync<T: Sync>() {}
|
||||
fn _需要_Send_Sync<T: Send + Sync>() {}
|
||||
|
||||
// 核心数据结构
|
||||
_需要_Send::<crate::kline::chan_kline::缠论K线>();
|
||||
_需要_Send::<crate::structure::dash_line::虚线>();
|
||||
_需要_Send::<crate::algorithm::hub::中枢>();
|
||||
_需要_Send_Sync::<crate::kline::chan_kline::缠论K线>();
|
||||
_需要_Send_Sync::<crate::structure::dash_line::虚线>();
|
||||
_需要_Send_Sync::<crate::algorithm::hub::中枢>();
|
||||
|
||||
// Arc 包装后的 Send 检查
|
||||
_需要_Send::<Arc<crate::kline::chan_kline::缠论K线>>();
|
||||
_需要_Send::<Arc<crate::structure::dash_line::虚线>>();
|
||||
_需要_Send::<Arc<crate::algorithm::hub::中枢>>();
|
||||
|
||||
// 观察者
|
||||
_需要_Send::<crate::business::observer::观察者>();
|
||||
}
|
||||
|
||||
/// 测试:Arc<缠论K线> 可跨线程传递
|
||||
#[test]
|
||||
fn test_跨线程_缠论K线_Send() {
|
||||
use crate::kline::bar::K线;
|
||||
use crate::kline::chan_kline::缠论K线;
|
||||
|
||||
let 普k = Arc::new(K线::创建普K(
|
||||
"test", 1000, 100.0, 110.0, 90.0, 95.0, 1000.0, 0, 300,
|
||||
));
|
||||
let ck = 缠论K线::创建缠K(
|
||||
1000,
|
||||
110.0,
|
||||
90.0,
|
||||
crate::types::相对方向::向上,
|
||||
None,
|
||||
1,
|
||||
普k,
|
||||
None,
|
||||
);
|
||||
let arc_ck = Arc::new(ck);
|
||||
let arc_ck2 = Arc::clone(&arc_ck);
|
||||
|
||||
let handle = std::thread::spawn(move || {
|
||||
let _ = arc_ck2;
|
||||
42
|
||||
});
|
||||
assert_eq!(handle.join().unwrap(), 42);
|
||||
assert!((arc_ck.高.get() - 110.0).abs() < 0.01);
|
||||
}
|
||||
|
||||
/// 测试:Arc<虚线> 可跨线程传递
|
||||
#[test]
|
||||
fn test_跨线程_虚线_Send() {
|
||||
use crate::kline::bar::K线;
|
||||
use crate::kline::chan_kline::缠论K线;
|
||||
use crate::structure::dash_line::虚线;
|
||||
use crate::structure::fractal_obj::分型;
|
||||
|
||||
let 普k = Arc::new(K线::创建普K(
|
||||
"test", 1000, 100.0, 110.0, 90.0, 95.0, 1000.0, 0, 300,
|
||||
));
|
||||
let ck = Arc::new(缠论K线::创建缠K(
|
||||
1000,
|
||||
110.0,
|
||||
90.0,
|
||||
crate::types::相对方向::向上,
|
||||
None,
|
||||
1,
|
||||
普k,
|
||||
None,
|
||||
));
|
||||
let frac = Arc::new(分型::new(None, Arc::clone(&ck), None));
|
||||
let frac2 = Arc::new(分型::new(None, ck, None));
|
||||
|
||||
let dash = Arc::new(虚线::创建笔(frac, frac2, true));
|
||||
let dash2 = Arc::clone(&dash);
|
||||
|
||||
let handle = std::thread::spawn(move || {
|
||||
let _ = Arc::as_ptr(&dash2.文);
|
||||
99
|
||||
});
|
||||
assert_eq!(handle.join().unwrap(), 99);
|
||||
assert_eq!(dash.标识.read().unwrap().as_str(), "笔");
|
||||
}
|
||||
|
||||
/// 测试:Arc<中枢> 可跨线程传递
|
||||
#[test]
|
||||
fn test_跨线程_中枢_Send() {
|
||||
let hub = crate::algorithm::hub::中枢::new(1, "test".into(), 1, vec![]);
|
||||
let arc_hub = Arc::new(hub);
|
||||
let arc_hub2 = Arc::clone(&arc_hub);
|
||||
|
||||
let handle = std::thread::spawn(move || {
|
||||
let _ = arc_hub2.序号.load(Ordering::Relaxed);
|
||||
77
|
||||
});
|
||||
assert_eq!(handle.join().unwrap(), 77);
|
||||
assert_eq!(arc_hub.序号.load(Ordering::Relaxed), 1);
|
||||
}
|
||||
|
||||
/// 测试:多线程并发读取 观察者
|
||||
#[test]
|
||||
fn test_跨线程_观察者_多线程读取() {
|
||||
let obs = 观察者::new("btcusd".into(), 86400, Default::default());
|
||||
let obs2 = Arc::clone(&obs);
|
||||
let obs3 = Arc::clone(&obs);
|
||||
|
||||
let h1 = std::thread::spawn(move || {
|
||||
let guard = obs2.read().unwrap();
|
||||
guard.符号.clone()
|
||||
});
|
||||
let h2 = std::thread::spawn(move || {
|
||||
let guard = obs3.read().unwrap();
|
||||
guard.周期
|
||||
});
|
||||
|
||||
assert_eq!(h1.join().unwrap(), "btcusd");
|
||||
assert_eq!(h2.join().unwrap(), 86400);
|
||||
}
|
||||
|
||||
/// 测试:Cell 字段跨线程读写不 panic
|
||||
#[test]
|
||||
fn test_跨线程_Cell字段_并发读写() {
|
||||
use crate::kline::bar::K线;
|
||||
use crate::kline::chan_kline::缠论K线;
|
||||
|
||||
let 普k = Arc::new(K线::创建普K(
|
||||
"test", 1000, 100.0, 110.0, 90.0, 95.0, 1000.0, 0, 300,
|
||||
));
|
||||
let ck = Arc::new(缠论K线::创建缠K(
|
||||
1000,
|
||||
110.0,
|
||||
90.0,
|
||||
crate::types::相对方向::向上,
|
||||
None,
|
||||
1,
|
||||
普k,
|
||||
None,
|
||||
));
|
||||
let ck2 = Arc::clone(&ck);
|
||||
|
||||
let handle = std::thread::spawn(move || {
|
||||
let 序号 = ck2.序号.load(Ordering::Relaxed);
|
||||
let 高 = ck2.高.get();
|
||||
(序号, 高)
|
||||
});
|
||||
|
||||
let (序号, 高) = handle.join().unwrap();
|
||||
assert_eq!(序号, 0); // 序号 初始值为 0
|
||||
assert!((高 - 110.0).abs() < 0.01);
|
||||
ck.序号.store(2, Ordering::Relaxed);
|
||||
assert_eq!(ck.序号.load(Ordering::Relaxed), 2);
|
||||
}
|
||||
|
||||
/// 测试:Arc<观察者> 直接跨线程传递
|
||||
#[test]
|
||||
fn test_跨线程_观察者_所有权转移() {
|
||||
let obs = 观察者::new("ethusd".into(), 7200, Default::default());
|
||||
|
||||
let handle = std::thread::spawn(move || {
|
||||
let guard = obs.read().unwrap();
|
||||
(guard.符号.clone(), guard.周期)
|
||||
});
|
||||
|
||||
let (符号, 周期) = handle.join().unwrap();
|
||||
assert_eq!(符号, "ethusd");
|
||||
assert_eq!(周期, 7200);
|
||||
}
|
||||
}
|
||||
|
||||
+90
-1
@@ -22,7 +22,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
|
||||
fn is_infinite_f64(v: &f64) -> bool {
|
||||
v.is_infinite()
|
||||
@@ -70,6 +70,7 @@ pub struct 缠论配置 {
|
||||
|
||||
// ---- 指标 ----
|
||||
pub 计算指标: bool,
|
||||
#[serde(deserialize_with = "deserialize_指标计算方式")]
|
||||
pub 指标计算方式: String,
|
||||
|
||||
// ---- MACD ----
|
||||
@@ -115,6 +116,7 @@ pub struct 缠论配置 {
|
||||
pub 买卖点激进识别: bool,
|
||||
pub 买卖点与MACD柱强相关: bool,
|
||||
pub 买卖点错过误差值: f64,
|
||||
#[serde(deserialize_with = "deserialize_买卖点_指标模式")]
|
||||
pub 买卖点_指标模式: String,
|
||||
pub 买卖点_指标匹配_MACD: bool,
|
||||
pub 买卖点_指标匹配_KDJ: bool,
|
||||
@@ -136,12 +138,66 @@ pub struct 缠论配置 {
|
||||
pub 线段内部背驰_MACD: bool,
|
||||
pub 线段内部背驰_斜率: bool,
|
||||
pub 线段内部背驰_测度: bool,
|
||||
#[serde(deserialize_with = "deserialize_线段内部背驰_模式")]
|
||||
pub 线段内部背驰_模式: String,
|
||||
|
||||
// ---- 文件 ----
|
||||
pub 加载文件路径: String,
|
||||
}
|
||||
|
||||
fn deserialize_指标计算方式<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
const VALID: &[&str] = &[
|
||||
"开",
|
||||
"高",
|
||||
"低",
|
||||
"收",
|
||||
"高低均值",
|
||||
"高低收均值",
|
||||
"开高低收均值",
|
||||
];
|
||||
const DEFAULT: &str = "收";
|
||||
if VALID.contains(&s.as_str()) {
|
||||
Ok(s)
|
||||
} else {
|
||||
eprintln!("\x1b[33m[配置警告]\x1b[m 指标计算方式: \"{s}\" 不在有效值 {VALID:?} 内,已使用默认值 \"{DEFAULT}\"");
|
||||
Ok(DEFAULT.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_买卖点_指标模式<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
const VALID: &[&str] = &["任意", "配置", "全量", "相对"];
|
||||
const DEFAULT: &str = "配置";
|
||||
if VALID.contains(&s.as_str()) {
|
||||
Ok(s)
|
||||
} else {
|
||||
eprintln!("\x1b[33m[配置警告]\x1b[m 买卖点_指标模式: \"{s}\" 不在有效值 {VALID:?} 内,已使用默认值 \"{DEFAULT}\"");
|
||||
Ok(DEFAULT.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_线段内部背驰_模式<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
const VALID: &[&str] = &["任意", "配置", "全量", "相对"];
|
||||
const DEFAULT: &str = "相对";
|
||||
if VALID.contains(&s.as_str()) {
|
||||
Ok(s)
|
||||
} else {
|
||||
eprintln!("\x1b[33m[配置警告]\x1b[m 线段内部背驰_模式: \"{s}\" 不在有效值 {VALID:?} 内,已使用默认值 \"{DEFAULT}\"");
|
||||
Ok(DEFAULT.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for 缠论配置 {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@@ -353,6 +409,39 @@ mod tests {
|
||||
assert_eq!(config.买卖点偏移, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_enum_field_fallback() {
|
||||
// 无效的 指标计算方式 → 回退默认值 "收"
|
||||
let json = r#"{"指标计算方式": "胡写"}"#;
|
||||
let config: 缠论配置 = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(config.指标计算方式, "收");
|
||||
|
||||
// 有效的 指标计算方式 → 正常通过
|
||||
let json = r#"{"指标计算方式": "开"}"#;
|
||||
let config: 缠论配置 = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(config.指标计算方式, "开");
|
||||
|
||||
// 无效的 买卖点_指标模式 → 回退默认值 "配置"
|
||||
let json = r#"{"买卖点_指标模式": "瞎搞"}"#;
|
||||
let config: 缠论配置 = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(config.买卖点_指标模式, "配置");
|
||||
|
||||
// 有效的 买卖点_指标模式 → 正常通过
|
||||
let json = r#"{"买卖点_指标模式": "任意"}"#;
|
||||
let config: 缠论配置 = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(config.买卖点_指标模式, "任意");
|
||||
|
||||
// 无效的 线段内部背驰_模式 → 回退默认值 "相对"
|
||||
let json = r#"{"线段内部背驰_模式": "乱来"}"#;
|
||||
let config: 缠论配置 = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(config.线段内部背驰_模式, "相对");
|
||||
|
||||
// 有效的 线段内部背驰_模式 → 正常通过
|
||||
let json = r#"{"线段内部背驰_模式": "全量"}"#;
|
||||
let config: 缠论配置 = serde_json::from_str(json).unwrap();
|
||||
assert_eq!(config.线段内部背驰_模式, "全量");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_不推送() {
|
||||
let config = 缠论配置::default();
|
||||
|
||||
@@ -28,7 +28,7 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// 原始K线 (OHLCV + 指标)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -202,12 +202,12 @@ impl 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);
|
||||
/// 截取Arc<K线>序列中从始到终的片段
|
||||
pub fn 截取rc(序列: &[Arc<Self>], 始: &Arc<Self>, 终: &Arc<Self>) -> Vec<Arc<Self>> {
|
||||
let 始_ptr = Arc::as_ptr(始);
|
||||
let 终_ptr = Arc::as_ptr(终);
|
||||
let 始_idx = 序列.iter().position(|k| Arc::as_ptr(k) == 始_ptr);
|
||||
let 终_idx = 序列.iter().position(|k| Arc::as_ptr(k) == 终_ptr);
|
||||
match (始_idx, 终_idx) {
|
||||
(Some(s), Some(e)) => 序列[s..=e].to_vec(),
|
||||
_ => Vec::new(),
|
||||
|
||||
+194
-164
@@ -30,24 +30,49 @@ use crate::kline::bar::K线;
|
||||
use crate::structure::fractal_obj::分型;
|
||||
use crate::types::分型结构;
|
||||
use crate::types::相对方向;
|
||||
use std::rc::Rc;
|
||||
use crate::types::SyncF64;
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
/// 缠论K线 — 经包含处理过后的K线
|
||||
#[derive(Debug, Clone)]
|
||||
///
|
||||
/// 部分字段使用 Cell/RefCell 实现内部可变性,确保包含处理原地修改时
|
||||
/// Rc 指针不变,所有持有该 Rc 的引用(如分型.右)能看到最新数据。
|
||||
#[derive(Debug)]
|
||||
pub struct 缠论K线 {
|
||||
pub 序号: i64,
|
||||
pub 时间戳: i64,
|
||||
pub 高: f64,
|
||||
pub 低: f64,
|
||||
pub 方向: 相对方向,
|
||||
pub 分型: Option<分型结构>,
|
||||
pub 序号: AtomicI64,
|
||||
pub 时间戳: AtomicI64,
|
||||
pub 高: SyncF64,
|
||||
pub 低: SyncF64,
|
||||
pub 方向: RwLock<相对方向>,
|
||||
pub 分型: RwLock<Option<分型结构>>,
|
||||
pub 周期: i64,
|
||||
pub 标识: String,
|
||||
pub 分型特征值: f64,
|
||||
pub 分型特征值: SyncF64,
|
||||
pub 原始起始序号: i64,
|
||||
pub 原始结束序号: i64,
|
||||
pub 标的K线: Rc<K线>,
|
||||
pub 买卖点信息: std::cell::RefCell<Vec<String>>,
|
||||
pub 原始结束序号: AtomicI64,
|
||||
pub 标的K线: RwLock<Arc<K线>>,
|
||||
pub 买卖点信息: RwLock<Vec<String>>,
|
||||
}
|
||||
|
||||
impl Clone for 缠论K线 {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
序号: AtomicI64::new(self.序号.load(Ordering::Relaxed)),
|
||||
时间戳: AtomicI64::new(self.时间戳.load(Ordering::Relaxed)),
|
||||
高: SyncF64::new(self.高.get()),
|
||||
低: SyncF64::new(self.低.get()),
|
||||
方向: RwLock::new(*self.方向.read().unwrap()),
|
||||
分型: RwLock::new(*self.分型.read().unwrap()),
|
||||
周期: self.周期,
|
||||
标识: self.标识.clone(),
|
||||
分型特征值: SyncF64::new(self.分型特征值.get()),
|
||||
原始起始序号: self.原始起始序号,
|
||||
原始结束序号: AtomicI64::new(self.原始结束序号.load(Ordering::Relaxed)),
|
||||
标的K线: RwLock::new(Arc::clone(&self.标的K线.read().unwrap())),
|
||||
买卖点信息: RwLock::new(self.买卖点信息.read().unwrap().clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for 缠论K线 {
|
||||
@@ -57,13 +82,16 @@ impl std::fmt::Display for 缠论K线 {
|
||||
f,
|
||||
"{}<{}, {}, {}, {}, {}, {}, {}>",
|
||||
self.标识,
|
||||
self.序号,
|
||||
self.分型.map_or("None".to_string(), |fx| fx.to_string()),
|
||||
self.序号.load(Ordering::Relaxed),
|
||||
self.分型
|
||||
.read()
|
||||
.unwrap()
|
||||
.map_or("None".to_string(), |fx| fx.to_string()),
|
||||
self.周期,
|
||||
self.方向,
|
||||
self.时间戳,
|
||||
format_f64_g(self.高),
|
||||
format_f64_g(self.低)
|
||||
*self.方向.read().unwrap(),
|
||||
self.时间戳.load(Ordering::Relaxed),
|
||||
format_f64_g(self.高.get()),
|
||||
format_f64_g(self.低.get())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -72,34 +100,34 @@ impl 缠论K线 {
|
||||
/// 创建镜像(浅拷贝 Rc 引用)
|
||||
pub fn 镜像(&self) -> Self {
|
||||
Self {
|
||||
序号: self.序号,
|
||||
时间戳: self.时间戳,
|
||||
高: self.高,
|
||||
低: self.低,
|
||||
方向: self.方向,
|
||||
分型: self.分型,
|
||||
序号: AtomicI64::new(self.序号.load(Ordering::Relaxed)),
|
||||
时间戳: AtomicI64::new(self.时间戳.load(Ordering::Relaxed)),
|
||||
高: SyncF64::new(self.高.get()),
|
||||
低: SyncF64::new(self.低.get()),
|
||||
方向: RwLock::new(*self.方向.read().unwrap()),
|
||||
分型: RwLock::new(*self.分型.read().unwrap()),
|
||||
周期: self.周期,
|
||||
标识: self.标识.clone(),
|
||||
分型特征值: self.分型特征值,
|
||||
分型特征值: SyncF64::new(self.分型特征值.get()),
|
||||
原始起始序号: self.原始起始序号,
|
||||
原始结束序号: self.原始结束序号,
|
||||
标的K线: Rc::clone(&self.标的K线),
|
||||
买卖点信息: std::cell::RefCell::new(self.买卖点信息.borrow().clone()),
|
||||
原始结束序号: AtomicI64::new(self.原始结束序号.load(Ordering::Relaxed)),
|
||||
标的K线: RwLock::new(Arc::clone(&self.标的K线.read().unwrap())),
|
||||
买卖点信息: RwLock::new(self.买卖点信息.read().unwrap().clone()),
|
||||
}
|
||||
}
|
||||
|
||||
/// 与MACD柱子匹配 — 底分型时MACD柱应<0, 顶分型时>0
|
||||
pub fn 与MACD柱子匹配(&self) -> bool {
|
||||
match self.分型 {
|
||||
match *self.分型.read().unwrap() {
|
||||
Some(分型结构::底) | Some(分型结构::下) => {
|
||||
if let Some(ref macd) = self.标的K线.macd {
|
||||
if let Some(ref macd) = self.标的K线.read().unwrap().macd {
|
||||
macd.MACD柱 < 0.0
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
Some(分型结构::顶) | Some(分型结构::上) => {
|
||||
if let Some(ref macd) = self.标的K线.macd {
|
||||
if let Some(ref macd) = self.标的K线.read().unwrap().macd {
|
||||
macd.MACD柱 > 0.0
|
||||
} else {
|
||||
false
|
||||
@@ -111,9 +139,9 @@ impl 缠论K线 {
|
||||
|
||||
/// 与RSI匹配 — 底分型时RSI应低于SMA, 顶分型时高于SMA
|
||||
pub fn 与RSI匹配(&self) -> bool {
|
||||
match self.分型 {
|
||||
match *self.分型.read().unwrap() {
|
||||
Some(分型结构::底) | Some(分型结构::下) => {
|
||||
if let Some(ref rsi) = self.标的K线.rsi {
|
||||
if let Some(ref rsi) = self.标的K线.read().unwrap().rsi {
|
||||
match (rsi.RSI, rsi.RSI_SMA) {
|
||||
(Some(r), Some(sma)) => r < sma,
|
||||
_ => false,
|
||||
@@ -123,7 +151,7 @@ impl 缠论K线 {
|
||||
}
|
||||
}
|
||||
Some(分型结构::顶) | Some(分型结构::上) => {
|
||||
if let Some(ref rsi) = self.标的K线.rsi {
|
||||
if let Some(ref rsi) = self.标的K线.read().unwrap().rsi {
|
||||
match (rsi.RSI, rsi.RSI_SMA) {
|
||||
(Some(r), Some(sma)) => r > sma,
|
||||
_ => false,
|
||||
@@ -138,9 +166,9 @@ impl 缠论K线 {
|
||||
|
||||
/// 与KDJ匹配 — 底分型时K应低于D(死叉后), 顶分型时K应高于D(金叉后)
|
||||
pub fn 与KDJ匹配(&self) -> bool {
|
||||
match self.分型 {
|
||||
match *self.分型.read().unwrap() {
|
||||
Some(分型结构::底) | Some(分型结构::下) => {
|
||||
if let Some(ref kdj) = self.标的K线.kdj {
|
||||
if let Some(ref kdj) = self.标的K线.read().unwrap().kdj {
|
||||
match (kdj.K, kdj.D) {
|
||||
(Some(k), Some(d)) => k < d,
|
||||
_ => false,
|
||||
@@ -150,7 +178,7 @@ impl 缠论K线 {
|
||||
}
|
||||
}
|
||||
Some(分型结构::顶) | Some(分型结构::上) => {
|
||||
if let Some(ref kdj) = self.标的K线.kdj {
|
||||
if let Some(ref kdj) = self.标的K线.read().unwrap().kdj {
|
||||
match (kdj.K, kdj.D) {
|
||||
(Some(k), Some(d)) => k > d,
|
||||
_ => false,
|
||||
@@ -164,25 +192,30 @@ impl 缠论K线 {
|
||||
}
|
||||
|
||||
/// 时间戳对齐 — 从基线序列中找匹配的时间戳
|
||||
pub fn 时间戳对齐(基线: &[Rc<缠论K线>], k线: &缠论K线) -> i64 {
|
||||
pub fn 时间戳对齐(基线: &[Arc<缠论K线>], k线: &缠论K线) -> i64 {
|
||||
if let Some(基) = 基线.first() {
|
||||
for k in 基线.iter().rev() {
|
||||
if 基.周期 < k线.周期 {
|
||||
if k线.时间戳 <= k.时间戳 && k.时间戳 <= k线.时间戳 + k线.周期
|
||||
if k线.时间戳.load(Ordering::Relaxed) <= k.时间戳.load(Ordering::Relaxed)
|
||||
&& k.时间戳.load(Ordering::Relaxed)
|
||||
<= k线.时间戳.load(Ordering::Relaxed) + k线.周期
|
||||
{
|
||||
if (k线.分型特征值 - k.分型特征值).abs() < f64::EPSILON {
|
||||
return k.时间戳;
|
||||
if (k线.分型特征值.get() - k.分型特征值.get()).abs() < f64::EPSILON
|
||||
{
|
||||
return k.时间戳.load(Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
} else if k.时间戳 <= k线.时间戳 && k线.时间戳 <= k.时间戳 + k.周期
|
||||
} else if k.时间戳.load(Ordering::Relaxed) <= k线.时间戳.load(Ordering::Relaxed)
|
||||
&& k线.时间戳.load(Ordering::Relaxed)
|
||||
<= k.时间戳.load(Ordering::Relaxed) + k.周期
|
||||
{
|
||||
if (k线.分型特征值 - k.分型特征值).abs() < f64::EPSILON {
|
||||
return k.时间戳;
|
||||
if (k线.分型特征值.get() - k.分型特征值.get()).abs() < f64::EPSILON {
|
||||
return k.时间戳.load(Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
k线.时间戳
|
||||
k线.时间戳.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// 创建缠K
|
||||
@@ -193,7 +226,7 @@ impl 缠论K线 {
|
||||
方向: 相对方向,
|
||||
结构: Option<分型结构>,
|
||||
原始序号: i64,
|
||||
普k: Rc<K线>,
|
||||
普k: Arc<K线>,
|
||||
之前: Option<&缠论K线>,
|
||||
) -> Self {
|
||||
if 高.is_nan() || 低.is_nan() {
|
||||
@@ -204,25 +237,28 @@ impl 缠论K线 {
|
||||
let 周期 = 普k.周期;
|
||||
let 标识 = 普k.标识.clone();
|
||||
|
||||
let mut 当前 = Self {
|
||||
序号: 0,
|
||||
时间戳,
|
||||
高,
|
||||
低,
|
||||
方向,
|
||||
分型: 结构,
|
||||
let 当前 = Self {
|
||||
序号: AtomicI64::new(0),
|
||||
时间戳: AtomicI64::new(时间戳),
|
||||
高: SyncF64::new(高),
|
||||
低: SyncF64::new(低),
|
||||
方向: RwLock::new(方向),
|
||||
分型: RwLock::new(结构),
|
||||
周期,
|
||||
标识,
|
||||
分型特征值: 高,
|
||||
分型特征值: SyncF64::new(高),
|
||||
原始起始序号: 原始序号,
|
||||
原始结束序号: 原始序号,
|
||||
标的K线: 普k,
|
||||
买卖点信息: std::cell::RefCell::new(Vec::new()),
|
||||
原始结束序号: AtomicI64::new(原始序号),
|
||||
标的K线: RwLock::new(普k),
|
||||
买卖点信息: RwLock::new(Vec::new()),
|
||||
};
|
||||
|
||||
if let Some(之前) = 之前 {
|
||||
当前.序号 = 之前.序号 + 1;
|
||||
let 关系 = 相对方向::分析(之前.高, 之前.低, 当前.高, 当前.低);
|
||||
当前
|
||||
.序号
|
||||
.store(之前.序号.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
|
||||
let 关系 =
|
||||
相对方向::分析(之前.高.get(), 之前.低.get(), 当前.高.get(), 当前.低.get());
|
||||
if 关系.是否包含() {
|
||||
panic!(
|
||||
"创建缠K 包含关系: {:?}\n 之前: {}\n 当前: {}",
|
||||
@@ -238,11 +274,11 @@ impl 缠论K线 {
|
||||
/// 返回 (新缠K, 模式) — 模式: "添加"/"替换"/None
|
||||
pub fn 兼并(
|
||||
之前缠K: Option<&缠论K线>,
|
||||
当前缠K: &mut 缠论K线,
|
||||
当前普K: &Rc<K线>,
|
||||
当前缠K: &缠论K线,
|
||||
当前普K: &Arc<K线>,
|
||||
配置: &缠论配置,
|
||||
) -> (Option<Rc<缠论K线>>, Option<String>) {
|
||||
let 关系 = 相对方向::分析(当前缠K.高, 当前缠K.低, 当前普K.高, 当前普K.低);
|
||||
) -> (Option<Arc<缠论K线>>, Option<String>) {
|
||||
let 关系 = 相对方向::分析(当前缠K.高.get(), 当前缠K.低.get(), 当前普K.高, 当前普K.低);
|
||||
|
||||
// 无包含关系 — 创建新元素追加
|
||||
if !关系.是否包含() {
|
||||
@@ -251,37 +287,47 @@ impl 缠论K线 {
|
||||
} else {
|
||||
Some(分型结构::上)
|
||||
};
|
||||
let mut 新缠K = Self::创建缠K(
|
||||
let 新缠K = Self::创建缠K(
|
||||
当前普K.时间戳,
|
||||
当前普K.高,
|
||||
当前普K.低,
|
||||
当前普K.方向(),
|
||||
结构,
|
||||
当前普K.序号,
|
||||
Rc::clone(当前普K),
|
||||
Arc::clone(当前普K),
|
||||
Some(当前缠K),
|
||||
);
|
||||
新缠K.序号 = 当前缠K.序号 + 1;
|
||||
return (Some(Rc::new(新缠K)), Some("添加".into()));
|
||||
新缠K
|
||||
.序号
|
||||
.store(当前缠K.序号.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
|
||||
return (Some(Arc::new(新缠K)), Some("添加".into()));
|
||||
}
|
||||
|
||||
// 重复提交检测 — 当序号相同时认为是重复提交K线
|
||||
if 当前普K.序号 == 当前缠K.原始结束序号 {
|
||||
if 当前普K.序号 == 当前缠K.原始结束序号.load(Ordering::Relaxed) {
|
||||
return (None, None);
|
||||
}
|
||||
|
||||
// 序号连续性检查
|
||||
if 当前普K.序号 - 1 != 当前缠K.原始结束序号 && 当前普K.序号 != 当前缠K.原始结束序号
|
||||
if 当前普K.序号 - 1 != 当前缠K.原始结束序号.load(Ordering::Relaxed)
|
||||
&& 当前普K.序号 != 当前缠K.原始结束序号.load(Ordering::Relaxed)
|
||||
{
|
||||
panic!(
|
||||
"兼并: 不可追加不连续元素 缠K.原始结束序号: {}, 当前普K.序号: {}",
|
||||
当前缠K.原始结束序号, 当前普K.序号
|
||||
当前缠K.原始结束序号.load(Ordering::Relaxed),
|
||||
当前普K.序号
|
||||
);
|
||||
}
|
||||
|
||||
// 包含关系 — 原地合并到当前缠K
|
||||
let 取值函数: fn(f64, f64) -> f64 = if let Some(之前) = 之前缠K {
|
||||
if 相对方向::分析(之前.高, 之前.低, 当前缠K.高, 当前缠K.低).是否向下()
|
||||
if 相对方向::分析(
|
||||
之前.高.get(),
|
||||
之前.低.get(),
|
||||
当前缠K.高.get(),
|
||||
当前缠K.低.get(),
|
||||
)
|
||||
.是否向下()
|
||||
{
|
||||
f64::min
|
||||
} else {
|
||||
@@ -293,20 +339,22 @@ impl 缠论K线 {
|
||||
|
||||
// 逆序包含时更新时间和标的K线
|
||||
if 关系 != 相对方向::顺 {
|
||||
当前缠K.时间戳 = 当前普K.时间戳;
|
||||
当前缠K.标的K线 = Rc::clone(当前普K);
|
||||
当前缠K.时间戳.store(当前普K.时间戳, Ordering::Relaxed);
|
||||
*当前缠K.标的K线.write().unwrap() = Arc::clone(当前普K);
|
||||
}
|
||||
当前缠K.高 = 取值函数(当前缠K.高, 当前普K.高);
|
||||
当前缠K.低 = 取值函数(当前缠K.低, 当前普K.低);
|
||||
当前缠K.原始结束序号 = 当前普K.序号;
|
||||
当前缠K.方向 = 当前普K.方向();
|
||||
当前缠K.高.set(取值函数(当前缠K.高.get(), 当前普K.高));
|
||||
当前缠K.低.set(取值函数(当前缠K.低.get(), 当前普K.低));
|
||||
当前缠K.原始结束序号.store(当前普K.序号, Ordering::Relaxed);
|
||||
*当前缠K.方向.write().unwrap() = 当前普K.方向();
|
||||
|
||||
if let Some(之前) = 之前缠K {
|
||||
当前缠K.序号 = 之前.序号 + 1;
|
||||
当前缠K
|
||||
.序号
|
||||
.store(之前.序号.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
if 配置.缠K合并替换 {
|
||||
(Some(Rc::new(当前缠K.镜像())), Some("替换".into()))
|
||||
(Some(Arc::new(当前缠K.镜像())), Some("替换".into()))
|
||||
} else {
|
||||
(None, None)
|
||||
}
|
||||
@@ -317,10 +365,10 @@ impl 缠论K线 {
|
||||
/// 返回 (状态, 形态)
|
||||
pub fn 分析(
|
||||
mut 当前K线: K线,
|
||||
缠K序列: &mut Vec<Rc<缠论K线>>,
|
||||
普K序列: &mut Vec<Rc<K线>>,
|
||||
缠K序列: &mut Vec<Arc<缠论K线>>,
|
||||
普K序列: &mut Vec<Arc<K线>>,
|
||||
配置: &缠论配置,
|
||||
) -> (String, Option<Rc<分型>>) {
|
||||
) -> (String, Option<Arc<分型>>) {
|
||||
当前K线.标识 = 配置.标识.clone();
|
||||
|
||||
// ---- 阶段1: 普K序列管理 + 指标增量计算 ----
|
||||
@@ -365,7 +413,7 @@ impl 缠论K线 {
|
||||
配置.随机指标_超卖阈值,
|
||||
));
|
||||
}
|
||||
let 当前K线_rc = Rc::new(当前K线);
|
||||
let 当前K线_rc = Arc::new(当前K线);
|
||||
普K序列.push(当前K线_rc);
|
||||
} else {
|
||||
let 之前普K = 普K序列.last().unwrap();
|
||||
@@ -412,7 +460,7 @@ impl 缠论K线 {
|
||||
}
|
||||
}
|
||||
普K序列.pop();
|
||||
普K序列.push(Rc::new(当前K线));
|
||||
普K序列.push(Arc::new(当前K线));
|
||||
} else {
|
||||
if 之前普K.时间戳 > 当前K线.时间戳 {
|
||||
panic!("时序错误: 之前={}, 当前={}", 之前普K.时间戳, 当前K线.时间戳);
|
||||
@@ -455,20 +503,20 @@ impl 缠论K线 {
|
||||
));
|
||||
}
|
||||
}
|
||||
普K序列.push(Rc::new(当前K线));
|
||||
普K序列.push(Arc::new(当前K线));
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 阶段2: 缠K合并 ----
|
||||
let 状态: String;
|
||||
let 当前K线_ref: &Rc<K线> = 普K序列.last().unwrap();
|
||||
let 当前K线_ref: &Arc<K线> = 普K序列.last().unwrap();
|
||||
|
||||
if !缠K序列.is_empty() {
|
||||
let len = 缠K序列.len();
|
||||
let (左边, 右边) = 缠K序列.split_at_mut(len - 1);
|
||||
let 之前缠K: Option<&缠论K线> = 左边.last().map(|rc| Rc::as_ref(rc));
|
||||
let 最后一个缠K_mut = Rc::make_mut(&mut 右边[0]);
|
||||
let (新缠K, 模式) = Self::兼并(之前缠K, 最后一个缠K_mut, 当前K线_ref, 配置);
|
||||
let 之前缠K: Option<&缠论K线> = 左边.last().map(|rc| Arc::as_ref(rc));
|
||||
let 最后一个缠K = &*右边[0];
|
||||
let (新缠K, 模式) = Self::兼并(之前缠K, 最后一个缠K, 当前K线_ref, 配置);
|
||||
|
||||
if let Some(k) = 新缠K {
|
||||
match 模式.as_deref() {
|
||||
@@ -477,9 +525,8 @@ impl 缠论K线 {
|
||||
状态 = "创建".into();
|
||||
}
|
||||
Some("替换") => {
|
||||
缠K序列.pop();
|
||||
缠K序列.push(k);
|
||||
状态 = "替换".into();
|
||||
// Cell::set 已原地更新数据,无需 pop+push 打破 Rc 身份
|
||||
状态 = "兼并".into();
|
||||
}
|
||||
_ => {
|
||||
状态 = "兼并".into();
|
||||
@@ -496,10 +543,10 @@ impl 缠论K线 {
|
||||
当前K线_ref.方向(),
|
||||
None,
|
||||
当前K线_ref.序号,
|
||||
Rc::clone(普K序列.last().unwrap()),
|
||||
Arc::clone(普K序列.last().unwrap()),
|
||||
None,
|
||||
);
|
||||
缠K序列.push(Rc::new(新缠K));
|
||||
缠K序列.push(Arc::new(新缠K));
|
||||
状态 = "新建".into();
|
||||
}
|
||||
|
||||
@@ -509,106 +556,89 @@ impl 缠论K线 {
|
||||
}
|
||||
|
||||
let idx = 缠K序列.len();
|
||||
let 左 = Rc::clone(&缠K序列[idx - 3]);
|
||||
let 中 = Rc::clone(&缠K序列[idx - 2]);
|
||||
let 右 = Rc::clone(&缠K序列[idx - 1]);
|
||||
let 左 = Arc::clone(&缠K序列[idx - 3]);
|
||||
let 中 = Arc::clone(&缠K序列[idx - 2]);
|
||||
let 右 = Arc::clone(&缠K序列[idx - 1]);
|
||||
|
||||
let 结构 = 分型结构::分析(&*左, &*中, &*右, false, false);
|
||||
|
||||
// 需要通过 Rc::get_mut 或 RefCell 修改 中.分型
|
||||
// 由于使用 Rc,中是不可变的。这里采用创建新 Rc 替换的方式。
|
||||
// 但这是在 Vec 内部修改,需要使用 Rc::make_mut 或重新构建
|
||||
// 对齐 Python:无条件设置 中.分型、中.分型特征值、右.分型特征值、右.分型
|
||||
*缠K序列[idx - 2].分型.write().unwrap() = 结构;
|
||||
|
||||
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(分型结构::底);
|
||||
}
|
||||
}
|
||||
分型结构::散 => {}
|
||||
match 结构 {
|
||||
分型结构::底 => {
|
||||
缠K序列[idx - 2].分型特征值.set(缠K序列[idx - 2].低.get());
|
||||
缠K序列[idx - 1].分型特征值.set(缠K序列[idx - 1].高.get());
|
||||
*缠K序列[idx - 1].分型.write().unwrap() = Some(分型结构::顶);
|
||||
}
|
||||
分型结构::顶 => {
|
||||
缠K序列[idx - 2].分型特征值.set(缠K序列[idx - 2].高.get());
|
||||
缠K序列[idx - 1].分型特征值.set(缠K序列[idx - 1].低.get());
|
||||
*缠K序列[idx - 1].分型.write().unwrap() = Some(分型结构::底);
|
||||
}
|
||||
分型结构::上 => {
|
||||
缠K序列[idx - 2].分型特征值.set(缠K序列[idx - 2].高.get());
|
||||
缠K序列[idx - 1].分型特征值.set(缠K序列[idx - 1].高.get());
|
||||
*缠K序列[idx - 1].分型.write().unwrap() = Some(分型结构::顶);
|
||||
}
|
||||
分型结构::下 => {
|
||||
缠K序列[idx - 2].分型特征值.set(缠K序列[idx - 2].低.get());
|
||||
缠K序列[idx - 1].分型特征值.set(缠K序列[idx - 1].低.get());
|
||||
*缠K序列[idx - 1].分型.write().unwrap() = Some(分型结构::底);
|
||||
}
|
||||
分型结构::散 => {}
|
||||
}
|
||||
|
||||
let 形态 = if matches!(结构, 分型结构::上 | 分型结构::下) {
|
||||
// Python: 形态 = 分型(中, 右, None) — 左=中K线, 中=右K线, 右=None
|
||||
Rc::new(分型::new(
|
||||
Some(Rc::clone(&缠K序列[idx - 2])),
|
||||
Rc::clone(&缠K序列[idx - 1]),
|
||||
Arc::new(分型::new(
|
||||
Some(Arc::clone(&缠K序列[idx - 2])),
|
||||
Arc::clone(&缠K序列[idx - 1]),
|
||||
None,
|
||||
))
|
||||
} else {
|
||||
Rc::new(分型::new(
|
||||
Some(Rc::clone(&缠K序列[idx - 3])),
|
||||
Rc::clone(&缠K序列[idx - 2]),
|
||||
Some(Rc::clone(&缠K序列[idx - 1])),
|
||||
Arc::new(分型::new(
|
||||
Some(Arc::clone(&缠K序列[idx - 3])),
|
||||
Arc::clone(&缠K序列[idx - 2]),
|
||||
Some(Arc::clone(&缠K序列[idx - 1])),
|
||||
))
|
||||
};
|
||||
|
||||
return (状态, Some(形态));
|
||||
}
|
||||
|
||||
(状态, None)
|
||||
// 对齐 Python:结构为 None 时仍创建并返回分型
|
||||
let 形态 = Arc::new(分型::new(
|
||||
Some(Arc::clone(&缠K序列[idx - 3])),
|
||||
Arc::clone(&缠K序列[idx - 2]),
|
||||
Some(Arc::clone(&缠K序列[idx - 1])),
|
||||
));
|
||||
return (状态, Some(形态));
|
||||
}
|
||||
|
||||
/// 截取缠K序列从始到终
|
||||
pub fn 截取(
|
||||
序列: &[Rc<缠论K线>], 始: &缠论K线, 终: &缠论K线
|
||||
) -> Option<Vec<Rc<缠论K线>>> {
|
||||
序列: &[Arc<缠论K线>],
|
||||
始: &缠论K线,
|
||||
终: &缠论K线,
|
||||
) -> Option<Vec<Arc<缠论K线>>> {
|
||||
let 始_idx = 序列
|
||||
.iter()
|
||||
.position(|k| Rc::as_ptr(k) == (始 as *const _))?;
|
||||
.position(|k| Arc::as_ptr(k) == (始 as *const _))?;
|
||||
let 终_idx = 序列
|
||||
.iter()
|
||||
.position(|k| Rc::as_ptr(k) == (终 as *const _))?;
|
||||
.position(|k| Arc::as_ptr(k) == (终 as *const _))?;
|
||||
Some(序列[始_idx..=终_idx].to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::types::fractal::有高低 for 缠论K线 {
|
||||
fn 高(&self) -> f64 {
|
||||
self.高
|
||||
self.高.get()
|
||||
}
|
||||
fn 低(&self) -> f64 {
|
||||
self.低
|
||||
self.低.get()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,11 +653,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_创建缠K_basic() {
|
||||
let pk = Rc::new(make_普K(1000, 100.0, 110.0, 95.0, 105.0, 0));
|
||||
let pk = Arc::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);
|
||||
assert_eq!(ck.高.get(), 110.0);
|
||||
assert_eq!(ck.低.get(), 95.0);
|
||||
assert_eq!(ck.序号.load(Ordering::Relaxed), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+2
-2
@@ -79,7 +79,7 @@ fn 测试_读取数据(文件路径: &str) {
|
||||
let 配置 = 缠论配置::default().不推送();
|
||||
match 观察者::读取数据文件(文件路径, Some(配置)) {
|
||||
Ok(观察员) => {
|
||||
let 观察员 = 观察员.borrow();
|
||||
let 观察员 = 观察员.read().unwrap();
|
||||
let 消耗用时 = 启动时间.elapsed();
|
||||
println!(
|
||||
"测试_读取数据 耗时 {:.2?} 普K数量 {}",
|
||||
@@ -162,7 +162,7 @@ fn 测试_周期合成(文件路径: &str) {
|
||||
// Display stats per period
|
||||
for &p in &[周期, 周期 * 5, 周期 * 5 * 6] {
|
||||
if let Some(观察员) = 多级别分析.获取观察者(p) {
|
||||
let 观察员 = 观察员.borrow();
|
||||
let 观察员 = 观察员.read().unwrap();
|
||||
println!(
|
||||
"周期<{}>: 缠K={}, 分型={}, 笔={}, 线段={}, 中枢={}",
|
||||
p,
|
||||
|
||||
+495
-135
@@ -29,30 +29,32 @@ use crate::kline::chan_kline::缠论K线;
|
||||
use crate::structure::fractal_obj::分型;
|
||||
use crate::structure::segment_feat::线段特征;
|
||||
use crate::types::{分型结构, 相对方向, 缺口};
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicBool, AtomicI64, Ordering};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
/// 虚线 — 笔和线段的通用数据结构
|
||||
///
|
||||
/// 笔和线段共享此 struct,通过 `标识` 字段区分 ("笔"/"线段"/"扩展线段"等)
|
||||
#[derive(Debug, Clone)]
|
||||
/// 可变字段使用 Cell/RefCell 实现内部可变性,确保 Rc 指针身份一致
|
||||
#[derive(Debug)]
|
||||
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,
|
||||
pub 标识: RwLock<String>,
|
||||
pub 序号: AtomicI64,
|
||||
pub 级别: AtomicI64,
|
||||
pub 文: Arc<分型>,
|
||||
pub 武: RwLock<Arc<分型>>,
|
||||
pub 有效性: AtomicBool,
|
||||
pub 基础序列: RwLock<Vec<Arc<虚线>>>,
|
||||
pub 特征序列: RwLock<Vec<Option<Arc<线段特征>>>>,
|
||||
pub 实_中枢序列: RwLock<Vec<Arc<中枢>>>,
|
||||
pub 虚_中枢序列: RwLock<Vec<Arc<中枢>>>,
|
||||
pub 合_中枢序列: RwLock<Vec<Arc<中枢>>>,
|
||||
pub 确认K线: RwLock<Option<Arc<缠论K线>>>,
|
||||
pub 模式: RwLock<String>,
|
||||
pub _特征序列_显示: AtomicBool,
|
||||
pub 前一缺口: RwLock<Option<缺口>>,
|
||||
pub 前一结束位置: RwLock<Option<Arc<虚线>>>,
|
||||
pub 短路修正: AtomicBool,
|
||||
}
|
||||
|
||||
/// MACD行为统计 — 统计MACD行为 方法的返回类型
|
||||
@@ -67,51 +69,73 @@ pub struct MACD行为统计 {
|
||||
pub 密集交叉区域: Vec<(usize, usize, usize)>,
|
||||
}
|
||||
|
||||
impl Clone for 虚线 {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
标识: RwLock::new(self.标识.read().unwrap().clone()),
|
||||
序号: AtomicI64::new(self.序号.load(Ordering::Relaxed)),
|
||||
级别: AtomicI64::new(self.级别.load(Ordering::Relaxed)),
|
||||
文: Arc::clone(&self.文),
|
||||
武: RwLock::new(Arc::clone(&self.武.read().unwrap())),
|
||||
有效性: AtomicBool::new(self.有效性.load(Ordering::Relaxed)),
|
||||
基础序列: RwLock::new(self.基础序列.read().unwrap().clone()),
|
||||
特征序列: RwLock::new(self.特征序列.read().unwrap().clone()),
|
||||
实_中枢序列: RwLock::new(self.实_中枢序列.read().unwrap().clone()),
|
||||
虚_中枢序列: RwLock::new(self.虚_中枢序列.read().unwrap().clone()),
|
||||
合_中枢序列: RwLock::new(self.合_中枢序列.read().unwrap().clone()),
|
||||
确认K线: RwLock::new(self.确认K线.read().unwrap().clone()),
|
||||
模式: RwLock::new(self.模式.read().unwrap().clone()),
|
||||
_特征序列_显示: AtomicBool::new(self._特征序列_显示.load(Ordering::Relaxed)),
|
||||
前一缺口: RwLock::new(*self.前一缺口.read().unwrap()),
|
||||
前一结束位置: RwLock::new(self.前一结束位置.read().unwrap().clone()),
|
||||
短路修正: AtomicBool::new(self.短路修正.load(Ordering::Relaxed)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl 虚线 {
|
||||
pub fn new(
|
||||
序号: i64,
|
||||
标识: String,
|
||||
文: Rc<分型>,
|
||||
武: Rc<分型>,
|
||||
文: Arc<分型>,
|
||||
武: Arc<分型>,
|
||||
级别: i64,
|
||||
有效性: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
序号,
|
||||
标识,
|
||||
级别,
|
||||
序号: AtomicI64::new(序号),
|
||||
标识: RwLock::new(标识),
|
||||
级别: AtomicI64::new(级别),
|
||||
文,
|
||||
武,
|
||||
有效性,
|
||||
基础序列: Vec::new(),
|
||||
特征序列: Vec::new(),
|
||||
实_中枢序列: Vec::new(),
|
||||
虚_中枢序列: Vec::new(),
|
||||
合_中枢序列: Vec::new(),
|
||||
确认K线: None,
|
||||
模式: "文武".into(),
|
||||
_特征序列_显示: false,
|
||||
前一缺口: None,
|
||||
前一结束位置: None,
|
||||
短路修正: false,
|
||||
武: RwLock::new(武),
|
||||
有效性: AtomicBool::new(有效性),
|
||||
基础序列: RwLock::new(Vec::new()),
|
||||
特征序列: RwLock::new(Vec::new()),
|
||||
实_中枢序列: RwLock::new(Vec::new()),
|
||||
虚_中枢序列: RwLock::new(Vec::new()),
|
||||
合_中枢序列: RwLock::new(Vec::new()),
|
||||
确认K线: RwLock::new(None),
|
||||
模式: RwLock::new("文武".into()),
|
||||
_特征序列_显示: AtomicBool::new(false),
|
||||
前一缺口: RwLock::new(None),
|
||||
前一结束位置: RwLock::new(None),
|
||||
短路修正: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
/// 笔序列(基础序列的别名)
|
||||
pub fn 笔序列(&self) -> &Vec<Rc<虚线>> {
|
||||
&self.基础序列
|
||||
}
|
||||
|
||||
pub fn 图表标题(&self) -> String {
|
||||
format!(
|
||||
"{}:{}:{}:{}",
|
||||
self.文.中.标识, self.文.中.周期, self.标识, self.序号
|
||||
self.文.中.标识,
|
||||
self.文.中.周期,
|
||||
self.标识.read().unwrap(),
|
||||
self.序号.load(Ordering::Relaxed)
|
||||
)
|
||||
}
|
||||
|
||||
/// 方向 — 文到武的方向
|
||||
pub fn 方向(&self) -> 相对方向 {
|
||||
match (self.文.结构, self.武.结构) {
|
||||
match (self.文.结构, self.武.read().unwrap().结构) {
|
||||
(分型结构::顶, 分型结构::底) => 相对方向::向下,
|
||||
(分型结构::顶, 分型结构::下) => 相对方向::向下,
|
||||
(分型结构::上, 分型结构::底) => 相对方向::向下,
|
||||
@@ -123,54 +147,60 @@ impl 虚线 {
|
||||
/// 虚线高
|
||||
pub fn 高(&self) -> f64 {
|
||||
if self.方向() == 相对方向::向下 {
|
||||
self.文.中.高
|
||||
self.文.中.高.get()
|
||||
} else {
|
||||
self.武.中.高
|
||||
self.武.read().unwrap().中.高.get()
|
||||
}
|
||||
}
|
||||
|
||||
/// 虚线低
|
||||
pub fn 低(&self) -> f64 {
|
||||
if self.方向() == 相对方向::向下 {
|
||||
self.武.中.低
|
||||
self.武.read().unwrap().中.低.get()
|
||||
} else {
|
||||
self.文.中.低
|
||||
self.文.中.低.get()
|
||||
}
|
||||
}
|
||||
|
||||
/// 判断两个虚线是否首尾相连
|
||||
pub fn 之前是(&self, 之前: &虚线) -> bool {
|
||||
if self.标识 != 之前.标识 {
|
||||
if *self.标识.read().unwrap() != *之前.标识.read().unwrap() {
|
||||
return false;
|
||||
}
|
||||
Rc::as_ptr(&之前.武) == Rc::as_ptr(&self.文)
|
||||
Arc::as_ptr(&*之前.武.read().unwrap()) == Arc::as_ptr(&self.文)
|
||||
}
|
||||
|
||||
/// 判断两个虚线是否首尾相连
|
||||
pub fn 之后是(&self, 之后: &虚线) -> bool {
|
||||
if self.标识 != 之后.标识 {
|
||||
if *self.标识.read().unwrap() != *之后.标识.read().unwrap() {
|
||||
return false;
|
||||
}
|
||||
Rc::as_ptr(&self.武) == Rc::as_ptr(&之后.文)
|
||||
Arc::as_ptr(&*self.武.read().unwrap()) == Arc::as_ptr(&之后.文)
|
||||
}
|
||||
|
||||
/// 获取该虚线范围内的普K序列
|
||||
pub fn 获取普K序列(&self, 普K序列: &[Rc<K线>]) -> Vec<Rc<K线>> {
|
||||
pub fn 获取普K序列(&self, 普K序列: &[Arc<K线>]) -> Vec<Arc<K线>> {
|
||||
// 使用指针查找(与 Python list.index 身份匹配行为一致),
|
||||
// 而非序号切片——因为序号可能与实际位置不一致。
|
||||
let 始 = 普K序列
|
||||
.iter()
|
||||
.position(|k| Rc::as_ptr(k) == Rc::as_ptr(&self.文.中.标的K线));
|
||||
let 终 = 普K序列
|
||||
.iter()
|
||||
.position(|k| Rc::as_ptr(k) == Rc::as_ptr(&self.武.中.标的K线));
|
||||
.position(|k| Arc::as_ptr(k) == Arc::as_ptr(&*self.文.中.标的K线.read().unwrap()));
|
||||
let 终 = 普K序列.iter().position(|k| {
|
||||
Arc::as_ptr(k) == Arc::as_ptr(&*self.武.read().unwrap().中.标的K线.read().unwrap())
|
||||
});
|
||||
match (始, 终) {
|
||||
(Some(s), Some(e)) if s <= e => 普K序列[s..=e].to_vec(),
|
||||
_ => {
|
||||
// 指针查找失败时回退到序号方式
|
||||
println!("[警告]虚线.获取普K序列 <指针查找失败时回退到序号方式>");
|
||||
let 始 = self.文.中.原始起始序号 as usize;
|
||||
let 终 = self.武.中.原始结束序号 as usize;
|
||||
let 终 = self
|
||||
.武
|
||||
.read()
|
||||
.unwrap()
|
||||
.中
|
||||
.原始结束序号
|
||||
.load(Ordering::Relaxed) as usize;
|
||||
if 始 < 普K序列.len() && 终 < 普K序列.len() && 始 <= 终 {
|
||||
普K序列[始..=终].to_vec()
|
||||
} else {
|
||||
@@ -181,36 +211,43 @@ impl 虚线 {
|
||||
}
|
||||
|
||||
/// 获取该虚线范围内的缠K序列
|
||||
pub fn 获取缠K序列(&self, 缠K序列: &[Rc<缠论K线>]) -> Vec<Rc<缠论K线>> {
|
||||
缠论K线::截取(缠K序列, &self.文.中, &self.武.中).unwrap_or_default()
|
||||
pub fn 获取缠K序列(&self, 缠K序列: &[Arc<缠论K线>]) -> Vec<Arc<缠论K线>> {
|
||||
缠论K线::截取(缠K序列, &self.文.中, &self.武.read().unwrap().中).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// 获取_武 — 递归获取虚线的终点分型(笔直接返回武,线段递归到底层笔的武)
|
||||
pub fn 获取_武(&self) -> Rc<分型> {
|
||||
if self.标识 == "笔" {
|
||||
return Rc::clone(&self.武);
|
||||
pub fn 获取_武(&self) -> Arc<分型> {
|
||||
if *self.标识.read().unwrap() == "笔" {
|
||||
return self.武.read().unwrap().clone();
|
||||
}
|
||||
let mut current: &虚线 = self;
|
||||
while current.标识 != "笔" {
|
||||
current = &*current.基础序列.last().unwrap();
|
||||
let mut current_rc = Arc::clone(self.基础序列.read().unwrap().last().unwrap());
|
||||
loop {
|
||||
if *current_rc.标识.read().unwrap() == "笔" {
|
||||
return current_rc.武.read().unwrap().clone();
|
||||
}
|
||||
let next = Arc::clone(current_rc.基础序列.read().unwrap().last().unwrap());
|
||||
current_rc = next;
|
||||
}
|
||||
Rc::clone(¤t.武)
|
||||
}
|
||||
|
||||
/// 获取数据文本(用于保存/调试)
|
||||
pub fn 获取数据文本(&self) -> String {
|
||||
use crate::utils::format_f64_g;
|
||||
if self.标识 == "笔" {
|
||||
if *self.标识.read().unwrap() == "笔" {
|
||||
return format!(
|
||||
"{}, {}, {}, 文:({},{}), 武:({},{}), {}",
|
||||
self.标识,
|
||||
self.序号,
|
||||
self.级别,
|
||||
self.标识.read().unwrap(),
|
||||
self.序号.load(Ordering::Relaxed),
|
||||
self.级别.load(Ordering::Relaxed),
|
||||
self.文.时间戳,
|
||||
format_f64_g(self.文.分型特征值),
|
||||
self.武.时间戳,
|
||||
format_f64_g(self.武.分型特征值),
|
||||
if self.有效性 { "True" } else { "False" },
|
||||
self.武.read().unwrap().时间戳,
|
||||
format_f64_g(self.武.read().unwrap().分型特征值),
|
||||
if self.有效性.load(Ordering::Relaxed) {
|
||||
"True"
|
||||
} else {
|
||||
"False"
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -225,11 +262,11 @@ impl 虚线 {
|
||||
}
|
||||
};
|
||||
|
||||
let 前一缺口_str = match &self.前一缺口 {
|
||||
let 前一缺口_str = match &*self.前一缺口.read().unwrap() {
|
||||
Some(g) => format!("{}", g),
|
||||
None => "None".to_string(),
|
||||
};
|
||||
let 前一结束位置_str = match &self.前一结束位置 {
|
||||
let 前一结束位置_str = match &*self.前一结束位置.read().unwrap() {
|
||||
Some(d) => format!("{}", d),
|
||||
None => "None".to_string(),
|
||||
};
|
||||
@@ -238,6 +275,8 @@ impl 虚线 {
|
||||
let 实_str = format!(
|
||||
"[{}]",
|
||||
self.实_中枢序列
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|h| format!("{}", h))
|
||||
.collect::<Vec<_>>()
|
||||
@@ -246,6 +285,8 @@ impl 虚线 {
|
||||
let 虚_str = format!(
|
||||
"[{}]",
|
||||
self.虚_中枢序列
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|h| format!("{}", h))
|
||||
.collect::<Vec<_>>()
|
||||
@@ -254,6 +295,8 @@ impl 虚线 {
|
||||
let 合_str = format!(
|
||||
"[{}]",
|
||||
self.合_中枢序列
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|h| format!("{}", h))
|
||||
.collect::<Vec<_>>()
|
||||
@@ -284,15 +327,15 @@ impl 虚线 {
|
||||
|
||||
format!(
|
||||
"{}, {}, {}, 文:({},{}), 武:({},{}), {}, {}, ({}, {}, {}), (前: {}, 后: {}, 三: {}, 伤: {}), 实: {}, 虚: {}, 合: {}, {}, {}, {}, {}",
|
||||
self.标识,
|
||||
self.序号,
|
||||
self.级别,
|
||||
self.标识.read().unwrap(),
|
||||
self.序号.load(Ordering::Relaxed),
|
||||
self.级别.load(Ordering::Relaxed),
|
||||
self.文.时间戳,
|
||||
format_f64_g(self.文.分型特征值),
|
||||
self.武.时间戳,
|
||||
format_f64_g(self.武.分型特征值),
|
||||
if self.有效性 { "True" } else { "False" },
|
||||
self.基础序列.len(),
|
||||
self.武.read().unwrap().时间戳,
|
||||
format_f64_g(self.武.read().unwrap().分型特征值),
|
||||
if self.有效性.load(Ordering::Relaxed) { "True" } else { "False" },
|
||||
self.基础序列.read().unwrap().len(),
|
||||
特征_bool(特征_a),
|
||||
特征_bool(特征_b),
|
||||
特征_bool(特征_c),
|
||||
@@ -303,33 +346,39 @@ impl 虚线 {
|
||||
实_str,
|
||||
虚_str,
|
||||
合_str,
|
||||
self.模式,
|
||||
self.模式.read().unwrap(),
|
||||
前一缺口_str,
|
||||
前一结束位置_str,
|
||||
if self.短路修正 { "True" } else { "False" },
|
||||
if self.短路修正.load(Ordering::Relaxed) { "True" } else { "False" },
|
||||
)
|
||||
}
|
||||
|
||||
// ---- 关联函数(静态工厂方法) ----
|
||||
|
||||
/// 创建笔
|
||||
pub fn 创建笔(文: Rc<分型>, 武: Rc<分型>, 有效性: bool) -> Self {
|
||||
pub fn 创建笔(文: Arc<分型>, 武: Arc<分型>, 有效性: bool) -> Self {
|
||||
Self::new(0, "笔".into(), 文, 武, 1, 有效性)
|
||||
}
|
||||
|
||||
/// 创建线段
|
||||
pub fn 创建线段(虚线序列: &[Rc<虚线>]) -> Self {
|
||||
let 文 = Rc::clone(&虚线序列[0].文);
|
||||
let 武 = Rc::clone(&虚线序列[虚线序列.len() - 1].武);
|
||||
let 标识 = if 虚线序列[0].标识 == "笔" {
|
||||
pub fn 创建线段(虚线序列: &[Arc<虚线>]) -> Self {
|
||||
let 文 = Arc::clone(&虚线序列[0].文);
|
||||
let 武 = Arc::clone(&*虚线序列[虚线序列.len() - 1].武.read().unwrap());
|
||||
assert!(
|
||||
文.结构 != 武.结构,
|
||||
"创建线段: 文.结构 == 武.结构 文={}, 武={}",
|
||||
文,
|
||||
武
|
||||
);
|
||||
let 标识: String = if *虚线序列[0].标识.read().unwrap() == "笔" {
|
||||
"线段".into()
|
||||
} else {
|
||||
format!("线段<{}>", 虚线序列[0].标识)
|
||||
format!("线段<{}>", 虚线序列[0].标识.read().unwrap())
|
||||
};
|
||||
let 级别 = 虚线序列[0].级别 + 1;
|
||||
let mut 段 = Self::new(0, 标识, 文, 武, 级别, true);
|
||||
段.基础序列 = 虚线序列.to_vec();
|
||||
段.模式 = "文武".into();
|
||||
let 级别 = 虚线序列[0].级别.load(Ordering::Relaxed) + 1;
|
||||
let 段 = Self::new(0, 标识, 文, 武, 级别, true);
|
||||
*段.基础序列.write().unwrap() = 虚线序列.to_vec();
|
||||
*段.模式.write().unwrap() = "文武".into();
|
||||
段
|
||||
}
|
||||
|
||||
@@ -383,8 +432,12 @@ impl 虚线 {
|
||||
// ---- MACD柱子均值计算 ----
|
||||
|
||||
/// 计算MACD柱子均值 — 虚线范围内所有MACD柱的绝对值均值
|
||||
pub fn 计算MACD柱子均值(普K序列: &[Rc<K线>], 实线: &虚线) -> f64 {
|
||||
let K线序列 = K线::截取rc(普K序列, &实线.文.中.标的K线, &实线.武.中.标的K线);
|
||||
pub fn 计算MACD柱子均值(普K序列: &[Arc<K线>], 实线: &虚线) -> f64 {
|
||||
let K线序列 = K线::截取rc(
|
||||
普K序列,
|
||||
&*实线.文.中.标的K线.read().unwrap(),
|
||||
&*实线.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
);
|
||||
if K线序列.is_empty() {
|
||||
return 0.0;
|
||||
}
|
||||
@@ -397,8 +450,12 @@ impl 虚线 {
|
||||
}
|
||||
|
||||
/// 计算MACD柱子均值_阴 — 负柱的绝对值均值
|
||||
pub fn 计算MACD柱子均值_阴(普K序列: &[Rc<K线>], 实线: &虚线) -> Option<f64> {
|
||||
let K线序列 = K线::截取rc(普K序列, &实线.文.中.标的K线, &实线.武.中.标的K线);
|
||||
pub fn 计算MACD柱子均值_阴(普K序列: &[Arc<K线>], 实线: &虚线) -> Option<f64> {
|
||||
let K线序列 = K线::截取rc(
|
||||
普K序列,
|
||||
&*实线.文.中.标的K线.read().unwrap(),
|
||||
&*实线.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
);
|
||||
let 总: Vec<f64> = K线序列
|
||||
.iter()
|
||||
.filter_map(|k| k.macd.as_ref())
|
||||
@@ -413,8 +470,12 @@ impl 虚线 {
|
||||
}
|
||||
|
||||
/// 计算MACD柱子均值_阳 — 正柱的绝对值均值
|
||||
pub fn 计算MACD柱子均值_阳(普K序列: &[Rc<K线>], 实线: &虚线) -> Option<f64> {
|
||||
let K线序列 = K线::截取rc(普K序列, &实线.文.中.标的K线, &实线.武.中.标的K线);
|
||||
pub fn 计算MACD柱子均值_阳(普K序列: &[Arc<K线>], 实线: &虚线) -> Option<f64> {
|
||||
let K线序列 = K线::截取rc(
|
||||
普K序列,
|
||||
&*实线.文.中.标的K线.read().unwrap(),
|
||||
&*实线.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
);
|
||||
let 总: Vec<f64> = K线序列
|
||||
.iter()
|
||||
.filter_map(|k| k.macd.as_ref())
|
||||
@@ -431,8 +492,10 @@ impl 虚线 {
|
||||
// ---- 武之MACD比较 ----
|
||||
|
||||
/// 武之全量MACD均值 — 武端MACD柱是否小于均值(背驰)
|
||||
pub fn 武之全量MACD均值(普K序列: &[Rc<K线>], 实线: &虚线) -> bool {
|
||||
let 武_MACD = match &实线.武.中.标的K线.macd {
|
||||
pub fn 武之全量MACD均值(普K序列: &[Arc<K线>], 实线: &虚线) -> bool {
|
||||
let 武_ref = 实线.武.read().unwrap();
|
||||
let 标 = 武_ref.中.标的K线.read().unwrap();
|
||||
let 武_MACD = match 标.macd.as_ref() {
|
||||
Some(m) => m.MACD柱.abs(),
|
||||
None => return false,
|
||||
};
|
||||
@@ -440,7 +503,7 @@ impl 虚线 {
|
||||
}
|
||||
|
||||
/// 武之MACD均值 — 按方向选择阴/阳均值比对
|
||||
pub fn 武之MACD均值(普K序列: &[Rc<K线>], 实线: &虚线) -> bool {
|
||||
pub fn 武之MACD均值(普K序列: &[Arc<K线>], 实线: &虚线) -> bool {
|
||||
if 实线.方向() == 相对方向::向上 {
|
||||
Self::武之MACD均值_阳(普K序列, 实线)
|
||||
} else {
|
||||
@@ -449,8 +512,10 @@ impl 虚线 {
|
||||
}
|
||||
|
||||
/// 武之MACD均值_阴 — 武端负柱是否小于阴均值
|
||||
pub fn 武之MACD均值_阴(普K序列: &[Rc<K线>], 实线: &虚线) -> bool {
|
||||
let 武_MACD = match &实线.武.中.标的K线.macd {
|
||||
pub fn 武之MACD均值_阴(普K序列: &[Arc<K线>], 实线: &虚线) -> bool {
|
||||
let 武_ref = 实线.武.read().unwrap();
|
||||
let 标 = 武_ref.中.标的K线.read().unwrap();
|
||||
let 武_MACD = match 标.macd.as_ref() {
|
||||
Some(m) => m.MACD柱.abs(),
|
||||
None => return false,
|
||||
};
|
||||
@@ -461,8 +526,10 @@ impl 虚线 {
|
||||
}
|
||||
|
||||
/// 武之MACD均值_阳 — 武端正柱是否小于阳均值
|
||||
pub fn 武之MACD均值_阳(普K序列: &[Rc<K线>], 实线: &虚线) -> bool {
|
||||
let 武_MACD = match &实线.武.中.标的K线.macd {
|
||||
pub fn 武之MACD均值_阳(普K序列: &[Arc<K线>], 实线: &虚线) -> bool {
|
||||
let 武_ref = 实线.武.read().unwrap();
|
||||
let 标 = 武_ref.中.标的K线.read().unwrap();
|
||||
let 武_MACD = match 标.macd.as_ref() {
|
||||
Some(m) => m.MACD柱.abs(),
|
||||
None => return false,
|
||||
};
|
||||
@@ -473,12 +540,18 @@ impl 虚线 {
|
||||
}
|
||||
|
||||
/// 武之MACD极值 — 武端MACD柱是否为区间极值
|
||||
pub fn 武之MACD极值(普K序列: &[Rc<K线>], 实线: &虚线) -> bool {
|
||||
let 武_MACD = match &实线.武.中.标的K线.macd {
|
||||
pub fn 武之MACD极值(普K序列: &[Arc<K线>], 实线: &虚线) -> bool {
|
||||
let 武_ref = 实线.武.read().unwrap();
|
||||
let 标 = 武_ref.中.标的K线.read().unwrap();
|
||||
let 武_MACD = match 标.macd.as_ref() {
|
||||
Some(m) => m.MACD柱,
|
||||
None => return false,
|
||||
};
|
||||
let K线序列 = K线::截取rc(普K序列, &实线.文.中.标的K线, &实线.武.中.标的K线);
|
||||
let K线序列 = K线::截取rc(
|
||||
普K序列,
|
||||
&*实线.文.中.标的K线.read().unwrap(),
|
||||
&*实线.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
);
|
||||
let 所有柱子: Vec<f64> = K线序列
|
||||
.iter()
|
||||
.filter_map(|k| k.macd.as_ref())
|
||||
@@ -500,7 +573,7 @@ impl 虚线 {
|
||||
|
||||
/// 计算K线序列MACD趋向背驰 — 分析 MACD柱/DIF/DEA 三项背驰信号
|
||||
pub fn 计算K线序列MACD趋向背驰(
|
||||
普K序列: &[Rc<K线>], 方向: 相对方向
|
||||
普K序列: &[Arc<K线>], 方向: 相对方向
|
||||
) -> [bool; 3] {
|
||||
if 普K序列.is_empty() {
|
||||
return [false, false, false];
|
||||
@@ -508,7 +581,7 @@ impl 虚线 {
|
||||
let 最后 = &普K序列[普K序列.len() - 1];
|
||||
|
||||
if 方向 == 相对方向::向上 {
|
||||
let 柱子序列: Vec<&Rc<K线>> = 普K序列
|
||||
let 柱子序列: Vec<&Arc<K线>> = 普K序列
|
||||
.iter()
|
||||
.filter(|k| k.macd.as_ref().map_or(false, |m| m.MACD柱 > 0.0))
|
||||
.collect();
|
||||
@@ -530,7 +603,7 @@ impl 虚线 {
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
})
|
||||
.unwrap();
|
||||
let mut 柱对 = vec![Rc::clone(*最高柱子), Rc::clone(最后)];
|
||||
let mut 柱对 = vec![Arc::clone(*最高柱子), Arc::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].高 {
|
||||
@@ -574,7 +647,7 @@ impl 虚线 {
|
||||
|
||||
结果
|
||||
} else {
|
||||
let 柱子序列: Vec<&Rc<K线>> = 普K序列
|
||||
let 柱子序列: Vec<&Arc<K线>> = 普K序列
|
||||
.iter()
|
||||
.filter(|k| k.macd.as_ref().map_or(false, |m| m.MACD柱 < 0.0))
|
||||
.collect();
|
||||
@@ -597,7 +670,7 @@ impl 虚线 {
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
})
|
||||
.unwrap();
|
||||
let mut 柱对 = vec![Rc::clone(*最高柱子), Rc::clone(最后)];
|
||||
let mut 柱对 = vec![Arc::clone(*最高柱子), Arc::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].低 {
|
||||
@@ -646,7 +719,7 @@ impl 虚线 {
|
||||
// ---- MACD柱子分段 ----
|
||||
|
||||
/// 计算MACD柱子分段 — 按正负号将MACD柱子分段
|
||||
pub fn 计算MACD柱子分段(k线序列: &[Rc<K线>]) -> Vec<Vec<f64>> {
|
||||
pub fn 计算MACD柱子分段(k线序列: &[Arc<K线>]) -> Vec<Vec<f64>> {
|
||||
if k线序列.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
@@ -729,7 +802,7 @@ impl 虚线 {
|
||||
|
||||
/// 统计MACD行为 — 分析DIF/DEA穿零轴和金叉死叉
|
||||
pub fn 统计MACD行为(
|
||||
普K序列: &[Rc<K线>],
|
||||
普K序列: &[Arc<K线>],
|
||||
最大间隔: usize,
|
||||
最少交叉数: usize,
|
||||
) -> MACD行为统计 {
|
||||
@@ -820,34 +893,39 @@ impl 虚线 {
|
||||
let 普K序列 = &观察员.普通K线序列;
|
||||
let 配置 = &观察员.配置;
|
||||
|
||||
if 实线.标识 != "笔" && 实线.标识 != "线段" && !实线.标识.starts_with("线段<")
|
||||
if *实线.标识.read().unwrap() != "笔"
|
||||
&& *实线.标识.read().unwrap() != "线段"
|
||||
&& !实线.标识.read().unwrap().starts_with("线段<")
|
||||
{
|
||||
return (false, "标识不在范围内".into());
|
||||
}
|
||||
|
||||
// KDJ指标完整性检查
|
||||
match &实线.武.中.标的K线.kdj {
|
||||
let 武_ref = 实线.武.read().unwrap();
|
||||
let 标 = 武_ref.中.标的K线.read().unwrap();
|
||||
match 标.kdj.as_ref() {
|
||||
Some(kdj) if kdj.K.is_some() && kdj.D.is_some() && kdj.J.is_some() => {}
|
||||
_ => return (false, "KDJ指标不完整".into()),
|
||||
}
|
||||
|
||||
let 意义 = Self::缠K买卖点模式(&配置.买卖点_指标模式, &实线.武.中, 配置);
|
||||
let 意义 =
|
||||
Self::缠K买卖点模式(&配置.买卖点_指标模式, &实线.武.read().unwrap().中, 配置);
|
||||
let 结果 = false;
|
||||
|
||||
let 背驰过: Vec<Rc<缠论K线>> = if 实线.标识 == "笔" {
|
||||
let 背驰过: Vec<Arc<缠论K线>> = if *实线.标识.read().unwrap() == "笔" {
|
||||
crate::algorithm::bi::笔::是否背驰过(实线, 观察员)
|
||||
} else {
|
||||
crate::algorithm::segment::线段::是否背驰过(实线, 观察员)
|
||||
};
|
||||
|
||||
if 意义 {
|
||||
if 实线.标识 == "笔" {
|
||||
if *实线.标识.read().unwrap() == "笔" {
|
||||
if Self::武之MACD均值(普K序列, 实线) {
|
||||
return (true, "武之MACD均值".into());
|
||||
}
|
||||
if Self::武之MACD极值(普K序列, 实线) && !背驰过.is_empty() {
|
||||
return (true, "背驰过且极值".into());
|
||||
} else if 实线.武.与MACD柱子分型匹配() {
|
||||
} else if 实线.武.read().unwrap().与MACD柱子分型匹配() {
|
||||
return (
|
||||
true,
|
||||
format!(
|
||||
@@ -862,14 +940,14 @@ impl 虚线 {
|
||||
);
|
||||
}
|
||||
}
|
||||
if 实线.标识 != "笔"
|
||||
if *实线.标识.read().unwrap() != "笔"
|
||||
&& crate::algorithm::segment::线段::判断线段内部是否背驰(实线, 观察员)
|
||||
{
|
||||
return (true, "线段内部背驰".into());
|
||||
}
|
||||
}
|
||||
|
||||
if !结果 && 意义 && 实线.武.中.与MACD柱子匹配() {
|
||||
if !结果 && 意义 && 实线.武.read().unwrap().中.与MACD柱子匹配() {
|
||||
if Self::武之MACD极值(普K序列, 实线) && 背驰过.len() > 2 {
|
||||
return (true, "没结果, 极值, 柱子分型匹配, 背驰过大于2次".into());
|
||||
}
|
||||
@@ -879,18 +957,300 @@ impl 虚线 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::kline::bar::K线;
|
||||
use crate::kline::chan_kline::缠论K线;
|
||||
use crate::types::分型结构;
|
||||
|
||||
/// 辅助:创建一根最小化的原始K线
|
||||
fn 辅助_创建K线(时间戳: i64, 高: f64, 低: f64, 开: f64, 收: f64) -> K线 {
|
||||
let mut k = K线::default();
|
||||
k.时间戳 = 时间戳;
|
||||
k.高 = 高;
|
||||
k.低 = 低;
|
||||
k.开盘价 = 开;
|
||||
k.收盘价 = 收;
|
||||
k
|
||||
}
|
||||
|
||||
/// 辅助:创建一根缠论K线
|
||||
fn 辅助_创建缠K(
|
||||
时间戳: i64,
|
||||
高: f64,
|
||||
低: f64,
|
||||
方向: 相对方向,
|
||||
结构: Option<分型结构>,
|
||||
序号: i64,
|
||||
) -> Arc<缠论K线> {
|
||||
let 普K = Arc::new(辅助_创建K线(时间戳, 高, 低, 低, 高));
|
||||
let 缠K = 缠论K线::创建缠K(时间戳, 高, 低, 方向, 结构, 序号, 普K, None);
|
||||
Arc::new(缠K)
|
||||
}
|
||||
|
||||
/// 辅助:创建顶分型
|
||||
fn 辅助_创建顶分型(时间戳: i64, 高: f64, 低: f64, 序号: i64) -> Arc<分型> {
|
||||
let 左 = 辅助_创建缠K(
|
||||
时间戳 - 2,
|
||||
高 - 2.0,
|
||||
低 - 2.0,
|
||||
相对方向::向上,
|
||||
Some(分型结构::上),
|
||||
序号 - 2,
|
||||
);
|
||||
let 中 = 辅助_创建缠K(时间戳, 高, 低, 相对方向::向上, Some(分型结构::顶), 序号);
|
||||
let 右 = 辅助_创建缠K(
|
||||
时间戳 + 2,
|
||||
高 - 1.0,
|
||||
低 - 1.0,
|
||||
相对方向::向下,
|
||||
Some(分型结构::下),
|
||||
序号 + 2,
|
||||
);
|
||||
Arc::new(分型::new(Some(左), 中, Some(右)))
|
||||
}
|
||||
|
||||
/// 辅助:创建底分型
|
||||
fn 辅助_创建底分型(时间戳: i64, 高: f64, 低: f64, 序号: i64) -> Arc<分型> {
|
||||
let 左 = 辅助_创建缠K(
|
||||
时间戳 - 2,
|
||||
高 + 2.0,
|
||||
低 + 2.0,
|
||||
相对方向::向下,
|
||||
Some(分型结构::下),
|
||||
序号 - 2,
|
||||
);
|
||||
let 中 = 缠论K线::创建缠K(
|
||||
时间戳,
|
||||
高,
|
||||
低,
|
||||
相对方向::向下,
|
||||
Some(分型结构::底),
|
||||
序号,
|
||||
Arc::new(辅助_创建K线(时间戳, 高, 低, 低, 高)),
|
||||
None,
|
||||
);
|
||||
中.分型特征值.set(低);
|
||||
let 中 = Arc::new(中);
|
||||
let 右 = 辅助_创建缠K(
|
||||
时间戳 + 2,
|
||||
高 + 1.0,
|
||||
低 + 1.0,
|
||||
相对方向::向上,
|
||||
Some(分型结构::上),
|
||||
序号 + 2,
|
||||
);
|
||||
Arc::new(分型::new(Some(左), 中, Some(右)))
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Cell 字段读写测试
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_Cell字段读写一致性() {
|
||||
let 顶 = 辅助_创建顶分型(100, 50.0, 40.0, 5);
|
||||
let 底 = 辅助_创建底分型(200, 30.0, 20.0, 10);
|
||||
let 笔 = 虚线::创建笔(顶, 底, true);
|
||||
|
||||
assert_eq!(笔.序号.load(Ordering::Relaxed), 0);
|
||||
assert!(笔.有效性.load(Ordering::Relaxed));
|
||||
assert!(!笔.短路修正.load(Ordering::Relaxed));
|
||||
assert!(笔.前一缺口.read().unwrap().is_none());
|
||||
|
||||
// 修改 Cell 字段
|
||||
笔.序号.store(42, Ordering::Relaxed);
|
||||
笔.有效性.store(false, Ordering::Relaxed);
|
||||
笔.短路修正.store(true, Ordering::Relaxed);
|
||||
*笔.前一缺口.write().unwrap() = Some(缺口::new(200.0, 100.0));
|
||||
|
||||
assert_eq!(笔.序号.load(Ordering::Relaxed), 42);
|
||||
assert!(!笔.有效性.load(Ordering::Relaxed));
|
||||
assert!(笔.短路修正.load(Ordering::Relaxed));
|
||||
let qk = 笔.前一缺口.read().unwrap().unwrap();
|
||||
assert!((qk.高 - 200.0).abs() < 0.01);
|
||||
assert!((qk.低 - 100.0).abs() < 0.01);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// RefCell 字段读写测试
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_RefCell字段读写一致性() {
|
||||
let 顶 = 辅助_创建顶分型(100, 50.0, 40.0, 5);
|
||||
let 底 = 辅助_创建底分型(200, 30.0, 20.0, 10);
|
||||
let 笔 = 虚线::创建笔(顶, 底, true);
|
||||
|
||||
// 标识
|
||||
assert_eq!(*笔.标识.read().unwrap(), "笔");
|
||||
*笔.标识.write().unwrap() = "测试标识".into();
|
||||
assert_eq!(*笔.标识.read().unwrap(), "测试标识");
|
||||
|
||||
// 模式
|
||||
assert_eq!(*笔.模式.read().unwrap(), "文武");
|
||||
*笔.模式.write().unwrap() = "全量".into();
|
||||
assert_eq!(*笔.模式.read().unwrap(), "全量");
|
||||
|
||||
// 基础序列
|
||||
assert!(笔.基础序列.read().unwrap().is_empty());
|
||||
let 另一底 = 辅助_创建底分型(300, 20.0, 10.0, 15);
|
||||
let 笔2 = 虚线::创建笔(Arc::clone(&*笔.武.read().unwrap()), 另一底, true);
|
||||
笔.基础序列.write().unwrap().push(Arc::new(笔2));
|
||||
assert_eq!(笔.基础序列.read().unwrap().len(), 1);
|
||||
|
||||
// 武 - Replace with new 分型
|
||||
let 新底 = 辅助_创建底分型(400, 15.0, 5.0, 20);
|
||||
let 新底_ptr = Arc::as_ptr(&新底);
|
||||
*笔.武.write().unwrap() = Arc::clone(&新底);
|
||||
assert_eq!(Arc::as_ptr(&*笔.武.read().unwrap()), 新底_ptr);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Clone 后 Rc 指针身份一致
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_虚线Clone后文Rc指针一致() {
|
||||
let 顶 = 辅助_创建顶分型(100, 50.0, 40.0, 5);
|
||||
let 底 = 辅助_创建底分型(200, 30.0, 20.0, 10);
|
||||
let 笔 = 虚线::创建笔(Arc::clone(&顶), Arc::clone(&底), true);
|
||||
|
||||
let 克隆笔 = 笔.clone();
|
||||
|
||||
// 文 Rc 指针应一致
|
||||
assert_eq!(Arc::as_ptr(&笔.文), Arc::as_ptr(&顶));
|
||||
assert_eq!(Arc::as_ptr(&克隆笔.文), Arc::as_ptr(&笔.文));
|
||||
|
||||
// 武 Rc 指针应一致
|
||||
assert_eq!(Arc::as_ptr(&*笔.武.read().unwrap()), Arc::as_ptr(&底));
|
||||
assert_eq!(
|
||||
Arc::as_ptr(&*克隆笔.武.read().unwrap()),
|
||||
Arc::as_ptr(&*笔.武.read().unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_虚线Clone是深拷贝Cell值而非共享() {
|
||||
let 顶 = 辅助_创建顶分型(100, 50.0, 40.0, 5);
|
||||
let 底 = 辅助_创建底分型(200, 30.0, 20.0, 10);
|
||||
let 笔 = 虚线::创建笔(顶, 底, true);
|
||||
笔.序号.store(10, Ordering::Relaxed);
|
||||
|
||||
let 克隆笔 = 笔.clone();
|
||||
// Clone 后序号应独立(deep copy for Cell)
|
||||
克隆笔.序号.store(99, Ordering::Relaxed);
|
||||
assert_eq!(笔.序号.load(Ordering::Relaxed), 10);
|
||||
assert_eq!(克隆笔.序号.load(Ordering::Relaxed), 99);
|
||||
|
||||
// Rc 指针仍应一致(文/武 共享)
|
||||
assert_eq!(Arc::as_ptr(&笔.文), Arc::as_ptr(&克隆笔.文));
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 多 Rc 共享下 Cell/RefCell 修改可见性
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_多Rc共享下Cell修改对所有引用可见() {
|
||||
let 顶 = 辅助_创建顶分型(100, 50.0, 40.0, 5);
|
||||
let 底 = 辅助_创建底分型(200, 30.0, 20.0, 10);
|
||||
let 笔_rc1 = Arc::new(虚线::创建笔(顶, 底, true));
|
||||
let 笔_rc2 = Arc::clone(&笔_rc1);
|
||||
|
||||
// 通过 rc1 修改 Cell
|
||||
笔_rc1.序号.store(77, Ordering::Relaxed);
|
||||
// rc2 应能看到
|
||||
assert_eq!(笔_rc2.序号.load(Ordering::Relaxed), 77);
|
||||
|
||||
// 通过 rc1 修改 RefCell
|
||||
*笔_rc1.模式.write().unwrap() = "配置".into();
|
||||
assert_eq!(*笔_rc2.模式.read().unwrap(), "配置");
|
||||
|
||||
// 通过 rc1 修改 武
|
||||
let 新底 = 辅助_创建底分型(400, 15.0, 5.0, 20);
|
||||
let 新底_ptr = Arc::as_ptr(&新底);
|
||||
*笔_rc1.武.write().unwrap() = Arc::clone(&新底);
|
||||
assert_eq!(Arc::as_ptr(&*笔_rc2.武.read().unwrap()), 新底_ptr);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 获取_武 递归正确性
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_获取武_笔级别直接返回武() {
|
||||
let 顶 = 辅助_创建顶分型(100, 50.0, 40.0, 5);
|
||||
let 底 = 辅助_创建底分型(200, 30.0, 20.0, 10);
|
||||
let 笔 = 虚线::创建笔(Arc::clone(&顶), Arc::clone(&底), true);
|
||||
|
||||
let wu = 笔.获取_武();
|
||||
assert_eq!(Arc::as_ptr(&*笔.武.read().unwrap()), Arc::as_ptr(&wu));
|
||||
assert_eq!(Arc::as_ptr(&wu), Arc::as_ptr(&底));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_获取武_线段级别递归到底层笔() {
|
||||
let 顶1 = 辅助_创建顶分型(100, 50.0, 40.0, 5);
|
||||
let 底1 = 辅助_创建底分型(200, 30.0, 20.0, 10);
|
||||
let 顶2 = 辅助_创建顶分型(300, 55.0, 45.0, 15);
|
||||
let 底2 = 辅助_创建底分型(400, 25.0, 15.0, 20);
|
||||
|
||||
let 笔1 = Arc::new(虚线::创建笔(Arc::clone(&顶1), Arc::clone(&底1), true));
|
||||
let 笔2 = Arc::new(虚线::创建笔(Arc::clone(&底1), Arc::clone(&顶2), true));
|
||||
let 笔3 = Arc::new(虚线::创建笔(Arc::clone(&顶2), Arc::clone(&底2), true));
|
||||
|
||||
let 段 = 虚线::创建线段(&[Arc::clone(&笔1), Arc::clone(&笔2), Arc::clone(&笔3)]);
|
||||
|
||||
// 线段的 获取_武 应返回底层最后一笔的武(底2)
|
||||
let wu = 段.获取_武();
|
||||
assert_eq!(Arc::as_ptr(&wu), Arc::as_ptr(&底2));
|
||||
|
||||
// 笔1 的 获取_武 应返回底1
|
||||
let wu1 = 笔1.获取_武();
|
||||
assert_eq!(Arc::as_ptr(&wu1), Arc::as_ptr(&底1));
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 虚线 字段原子性 - 修改不影响文(不可变字段)
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_修改武不影响文Rc指针() {
|
||||
let 顶 = 辅助_创建顶分型(100, 50.0, 40.0, 5);
|
||||
let 底1 = 辅助_创建底分型(200, 30.0, 20.0, 10);
|
||||
let 底2 = 辅助_创建底分型(300, 25.0, 15.0, 15);
|
||||
|
||||
let 笔 = 虚线::创建笔(Arc::clone(&顶), Arc::clone(&底1), true);
|
||||
let 文_ptr_before = Arc::as_ptr(&笔.文);
|
||||
|
||||
// 修改武
|
||||
*笔.武.write().unwrap() = Arc::clone(&底2);
|
||||
|
||||
// 文指针不变
|
||||
assert_eq!(Arc::as_ptr(&笔.文), 文_ptr_before);
|
||||
|
||||
// 但方向变了(因为武从底1变成底2)
|
||||
let 新武耗时 = 笔.武.read().unwrap().时间戳;
|
||||
assert_eq!(新武耗时, 300);
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for 虚线 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.标识 == "笔" {
|
||||
if *self.标识.read().unwrap() == "笔" {
|
||||
write!(
|
||||
f,
|
||||
"笔({}, {}, {}, {}, 周期: {}, 数量: {})",
|
||||
self.序号,
|
||||
self.序号.load(Ordering::Relaxed),
|
||||
self.方向(),
|
||||
self.文,
|
||||
self.武,
|
||||
self.武.read().unwrap(),
|
||||
self.文.中.周期,
|
||||
self.武.中.序号 - self.文.中.序号 + 1
|
||||
self.武.read().unwrap().中.序号.load(Ordering::Relaxed)
|
||||
- self.文.中.序号.load(Ordering::Relaxed)
|
||||
+ 1
|
||||
)
|
||||
} else {
|
||||
let 四象 = crate::algorithm::segment::线段::四象(self);
|
||||
@@ -899,20 +1259,20 @@ impl std::fmt::Display for 虚线 {
|
||||
Some(g) => format!("{}", g),
|
||||
None => "None".to_string(),
|
||||
};
|
||||
let 确认K线_str = match &self.确认K线 {
|
||||
let 确认K线_str = match &*self.确认K线.read().unwrap() {
|
||||
Some(k) => format!("{}", k),
|
||||
None => "None".to_string(),
|
||||
};
|
||||
write!(
|
||||
f,
|
||||
"{}<{}, {}, {}, {}, {}, 数量: {}, 缺口: {}, {}>",
|
||||
self.标识,
|
||||
self.序号,
|
||||
self.标识.read().unwrap(),
|
||||
self.序号.load(Ordering::Relaxed),
|
||||
四象,
|
||||
self.方向(),
|
||||
self.文,
|
||||
self.武,
|
||||
self.基础序列.len(),
|
||||
self.武.read().unwrap(),
|
||||
self.基础序列.read().unwrap().len(),
|
||||
缺口_str,
|
||||
确认K线_str,
|
||||
)
|
||||
|
||||
@@ -24,20 +24,20 @@
|
||||
|
||||
use crate::structure::segment_feat::线段特征;
|
||||
use crate::types::分型结构;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// 特征分型 — 由三个线段特征元素构成的分型
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct 特征分型 {
|
||||
pub 左: Rc<线段特征>,
|
||||
pub 中: Rc<线段特征>,
|
||||
pub 右: Rc<线段特征>,
|
||||
pub 左: Arc<线段特征>,
|
||||
pub 中: Arc<线段特征>,
|
||||
pub 右: Arc<线段特征>,
|
||||
pub 结构: 分型结构,
|
||||
}
|
||||
|
||||
impl 特征分型 {
|
||||
pub fn new(
|
||||
左: Rc<线段特征>, 中: Rc<线段特征>, 右: Rc<线段特征>, 结构: 分型结构
|
||||
左: Arc<线段特征>, 中: Arc<线段特征>, 右: Arc<线段特征>, 结构: 分型结构
|
||||
) -> Self {
|
||||
Self {
|
||||
左, 中, 右, 结构
|
||||
|
||||
@@ -25,14 +25,15 @@
|
||||
use crate::kline::chan_kline::缠论K线;
|
||||
use crate::types::分型结构;
|
||||
use crate::types::相对方向;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// 分型 — 由三根缠K构成(可能缺左或右)
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct 分型 {
|
||||
pub 左: Option<Rc<缠论K线>>,
|
||||
pub 中: Rc<缠论K线>,
|
||||
pub 右: Option<Rc<缠论K线>>,
|
||||
pub 左: Option<Arc<缠论K线>>,
|
||||
pub 中: Arc<缠论K线>,
|
||||
pub 右: Option<Arc<缠论K线>>,
|
||||
pub 结构: 分型结构,
|
||||
pub 时间戳: i64,
|
||||
pub 分型特征值: f64,
|
||||
@@ -40,11 +41,11 @@ pub struct 分型 {
|
||||
|
||||
impl 分型 {
|
||||
pub fn new(
|
||||
左: Option<Rc<缠论K线>>, 中: Rc<缠论K线>, 右: Option<Rc<缠论K线>>
|
||||
左: Option<Arc<缠论K线>>, 中: Arc<缠论K线>, 右: Option<Arc<缠论K线>>
|
||||
) -> Self {
|
||||
let 结构 = 中.分型.unwrap_or(分型结构::散);
|
||||
let 时间戳 = 中.时间戳;
|
||||
let 分型特征值 = 中.分型特征值;
|
||||
let 结构 = 中.分型.read().unwrap().unwrap_or(分型结构::散);
|
||||
let 时间戳 = 中.时间戳.load(Ordering::Relaxed);
|
||||
let 分型特征值 = 中.分型特征值.get();
|
||||
Self {
|
||||
左,
|
||||
中,
|
||||
@@ -60,9 +61,9 @@ impl 分型 {
|
||||
let 左 = self.左.as_ref()?;
|
||||
let 右 = self.右.as_ref()?;
|
||||
Some((
|
||||
相对方向::分析(左.高, 左.低, self.中.高, self.中.低),
|
||||
相对方向::分析(self.中.高, self.中.低, 右.高, 右.低),
|
||||
相对方向::分析(左.高, 左.低, 右.高, 右.低),
|
||||
相对方向::分析(左.高.get(), 左.低.get(), self.中.高.get(), self.中.低.get()),
|
||||
相对方向::分析(self.中.高.get(), self.中.低.get(), 右.高.get(), 右.低.get()),
|
||||
相对方向::分析(左.高.get(), 左.低.get(), 右.高.get(), 右.低.get()),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -97,17 +98,19 @@ impl 分型 {
|
||||
|
||||
if let (Some(ref 左), Some(ref 右)) = (&self.左, &self.右) {
|
||||
if self.结构 == 分型结构::底 {
|
||||
if 右.标的K线.收盘价 > 左.标的K线.高 {
|
||||
if 右.标的K线.read().unwrap().收盘价 > 左.标的K线.read().unwrap().高 {
|
||||
return "强";
|
||||
} else if 右.标的K线.收盘价 > self.中.标的K线.高 {
|
||||
} else if 右.标的K线.read().unwrap().收盘价 > self.中.标的K线.read().unwrap().高
|
||||
{
|
||||
return "中";
|
||||
} else {
|
||||
return "弱";
|
||||
}
|
||||
} else if self.结构 == 分型结构::顶 {
|
||||
if 右.标的K线.收盘价 < 左.标的K线.低 {
|
||||
if 右.标的K线.read().unwrap().收盘价 < 左.标的K线.read().unwrap().低 {
|
||||
return "强";
|
||||
} else if 右.标的K线.收盘价 < self.中.标的K线.低 {
|
||||
} else if 右.标的K线.read().unwrap().收盘价 < self.中.标的K线.read().unwrap().低
|
||||
{
|
||||
return "中";
|
||||
} else {
|
||||
return "弱";
|
||||
@@ -121,15 +124,21 @@ impl 分型 {
|
||||
pub fn 与MACD柱子分型匹配(&self) -> bool {
|
||||
if let (Some(ref 左), Some(ref 右)) = (&self.左, &self.右) {
|
||||
if self.结构 == 分型结构::底 {
|
||||
let 左_k = 左.标的K线.read().unwrap();
|
||||
let 中_k = self.中.标的K线.read().unwrap();
|
||||
let 右_k = 右.标的K线.read().unwrap();
|
||||
if let (Some(ref 左macd), Some(ref 中macd), Some(ref 右macd)) =
|
||||
(&左.标的K线.macd, &self.中.标的K线.macd, &右.标的K线.macd)
|
||||
(&左_k.macd, &中_k.macd, &右_k.macd)
|
||||
{
|
||||
return 左macd.MACD柱 > 中macd.MACD柱 && 中macd.MACD柱 < 右macd.MACD柱;
|
||||
}
|
||||
}
|
||||
if self.结构 == 分型结构::顶 {
|
||||
let 左_k = 左.标的K线.read().unwrap();
|
||||
let 中_k = self.中.标的K线.read().unwrap();
|
||||
let 右_k = 右.标的K线.read().unwrap();
|
||||
if let (Some(ref 左macd), Some(ref 中macd), Some(ref 右macd)) =
|
||||
(&左.标的K线.macd, &self.中.标的K线.macd, &右.标的K线.macd)
|
||||
(&左_k.macd, &中_k.macd, &右_k.macd)
|
||||
{
|
||||
return 左macd.MACD柱 < 中macd.MACD柱 && 中macd.MACD柱 > 右macd.MACD柱;
|
||||
}
|
||||
@@ -139,35 +148,36 @@ impl 分型 {
|
||||
}
|
||||
|
||||
/// 判断两个分型是否匹配
|
||||
pub fn 判断分型(左: &Rc<分型>, 右: &Rc<分型>, 模式: &str) -> bool {
|
||||
pub fn 判断分型(左: &Arc<分型>, 右: &Arc<分型>, 模式: &str) -> bool {
|
||||
match 模式 {
|
||||
"中" => Rc::as_ptr(左) == Rc::as_ptr(右),
|
||||
"中" => Arc::as_ptr(左) == Arc::as_ptr(右),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// 从缠K序列中获取以指定缠K为中元素的分型
|
||||
pub fn 从缠K序列中获取分型(
|
||||
K线序列: &[Rc<缠论K线>], 中: &Rc<缠论K线>
|
||||
K线序列: &[Arc<缠论K线>],
|
||||
中: &Arc<缠论K线>,
|
||||
) -> Option<Self> {
|
||||
let idx = K线序列
|
||||
.iter()
|
||||
.position(|k| Rc::as_ptr(k) == Rc::as_ptr(中))?;
|
||||
.position(|k| Arc::as_ptr(k) == Arc::as_ptr(中))?;
|
||||
let 左 = if idx > 0 {
|
||||
Some(Rc::clone(&K线序列[idx - 1]))
|
||||
Some(Arc::clone(&K线序列[idx - 1]))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let 右 = if idx + 1 < K线序列.len() {
|
||||
Some(Rc::clone(&K线序列[idx + 1]))
|
||||
Some(Arc::clone(&K线序列[idx + 1]))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(Self::new(左, Rc::clone(中), 右))
|
||||
Some(Self::new(左, Arc::clone(中), 右))
|
||||
}
|
||||
|
||||
/// 向分型序列中添加新分型
|
||||
pub fn 向序列中添加(分型序列: &mut Vec<Rc<分型>>, 当前分型: Rc<分型>) {
|
||||
pub fn 向序列中添加(分型序列: &mut Vec<Arc<分型>>, 当前分型: Arc<分型>) {
|
||||
if 分型序列.is_empty() {
|
||||
if 当前分型.结构 != 分型结构::顶 && 当前分型.结构 != 分型结构::底
|
||||
{
|
||||
@@ -188,10 +198,10 @@ impl 分型 {
|
||||
|
||||
impl crate::types::fractal::有高低 for 分型 {
|
||||
fn 高(&self) -> f64 {
|
||||
self.中.高
|
||||
self.中.高.get()
|
||||
}
|
||||
fn 低(&self) -> f64 {
|
||||
self.中.低
|
||||
self.中.低.get()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +210,11 @@ impl std::fmt::Display for 分型 {
|
||||
write!(
|
||||
f,
|
||||
"{}<{}, {}, None: {}, None: {}>",
|
||||
self.中.分型.unwrap_or(crate::types::分型结构::散),
|
||||
self.中
|
||||
.分型
|
||||
.read()
|
||||
.unwrap()
|
||||
.unwrap_or(crate::types::分型结构::散),
|
||||
self.时间戳,
|
||||
crate::utils::format_f64_g(self.分型特征值),
|
||||
if self.左.is_none() { "True" } else { "False" },
|
||||
|
||||
@@ -26,7 +26,8 @@ use crate::structure::dash_line::虚线;
|
||||
use crate::structure::feat_fractal::特征分型;
|
||||
use crate::structure::fractal_obj::分型;
|
||||
use crate::types::{分型结构, 相对方向};
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// 线段特征 — 特征序列元素(内部是虚线的集合)
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -34,11 +35,11 @@ pub struct 线段特征 {
|
||||
pub 序号: i64,
|
||||
pub 标识: String,
|
||||
pub 线段方向: 相对方向,
|
||||
pub 元素: Vec<Rc<虚线>>,
|
||||
pub 元素: Vec<Arc<虚线>>,
|
||||
}
|
||||
|
||||
impl 线段特征 {
|
||||
pub fn new(标识: String, 基础序列: Vec<Rc<虚线>>, 线段方向: 相对方向) -> Self {
|
||||
pub fn new(标识: String, 基础序列: Vec<Arc<虚线>>, 线段方向: 相对方向) -> Self {
|
||||
Self {
|
||||
序号: 0,
|
||||
标识,
|
||||
@@ -53,7 +54,7 @@ impl 线段特征 {
|
||||
|
||||
/// 文 — 取特征序列元素中分型特征值最大/最小的文分型
|
||||
/// tiebreaker: later时间戳 wins when特征值 equal (matches Python)
|
||||
pub fn 文(&self) -> Rc<分型> {
|
||||
pub fn 文(&self) -> Arc<分型> {
|
||||
if self.线段方向.是否向上() {
|
||||
self.元素
|
||||
.iter()
|
||||
@@ -64,8 +65,8 @@ impl 线段特征 {
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
.then_with(|| a.文.时间戳.cmp(&b.文.时间戳))
|
||||
})
|
||||
.map(|x| Rc::clone(&x.文))
|
||||
.unwrap_or_else(|| Rc::clone(&self.元素[0].文))
|
||||
.map(|x| Arc::clone(&x.文))
|
||||
.unwrap_or_else(|| Arc::clone(&self.元素[0].文))
|
||||
} else {
|
||||
self.元素
|
||||
.iter()
|
||||
@@ -76,38 +77,54 @@ impl 线段特征 {
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
.then_with(|| b.文.时间戳.cmp(&a.文.时间戳))
|
||||
})
|
||||
.map(|x| Rc::clone(&x.文))
|
||||
.unwrap_or_else(|| Rc::clone(&self.元素[0].文))
|
||||
.map(|x| Arc::clone(&x.文))
|
||||
.unwrap_or_else(|| Arc::clone(&self.元素[0].文))
|
||||
}
|
||||
}
|
||||
|
||||
/// 武 — 取特征序列元素中分型特征值最大/最小的武分型
|
||||
/// tiebreaker: later时间戳 wins when特征值 equal (matches Python)
|
||||
pub fn 武(&self) -> Rc<分型> {
|
||||
pub fn 武(&self) -> Arc<分型> {
|
||||
if self.线段方向.是否向上() {
|
||||
self.元素
|
||||
.iter()
|
||||
.max_by(|a, b| {
|
||||
a.武
|
||||
.read()
|
||||
.unwrap()
|
||||
.分型特征值
|
||||
.partial_cmp(&b.武.分型特征值)
|
||||
.partial_cmp(&b.武.read().unwrap().分型特征值)
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
.then_with(|| a.武.时间戳.cmp(&b.武.时间戳))
|
||||
.then_with(|| {
|
||||
a.武
|
||||
.read()
|
||||
.unwrap()
|
||||
.时间戳
|
||||
.cmp(&b.武.read().unwrap().时间戳)
|
||||
})
|
||||
})
|
||||
.map(|x| Rc::clone(&x.武))
|
||||
.unwrap_or_else(|| Rc::clone(&self.元素[0].武))
|
||||
.map(|x| x.武.read().unwrap().clone())
|
||||
.unwrap_or_else(|| self.元素[0].武.read().unwrap().clone())
|
||||
} else {
|
||||
self.元素
|
||||
.iter()
|
||||
.min_by(|a, b| {
|
||||
a.武
|
||||
.read()
|
||||
.unwrap()
|
||||
.分型特征值
|
||||
.partial_cmp(&b.武.分型特征值)
|
||||
.partial_cmp(&b.武.read().unwrap().分型特征值)
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
.then_with(|| b.武.时间戳.cmp(&a.武.时间戳))
|
||||
.then_with(|| {
|
||||
b.武
|
||||
.read()
|
||||
.unwrap()
|
||||
.时间戳
|
||||
.cmp(&a.武.read().unwrap().时间戳)
|
||||
})
|
||||
})
|
||||
.map(|x| Rc::clone(&x.武))
|
||||
.unwrap_or_else(|| Rc::clone(&self.元素[0].武))
|
||||
.map(|x| x.武.read().unwrap().clone())
|
||||
.unwrap_or_else(|| self.元素[0].武.read().unwrap().clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +146,7 @@ impl 线段特征 {
|
||||
}
|
||||
|
||||
/// 向特征序列元素中添加虚线
|
||||
pub fn 添加(&mut self, 待添加虚线: Rc<虚线>) -> Result<(), String> {
|
||||
pub fn 添加(&mut self, 待添加虚线: Arc<虚线>) -> Result<(), String> {
|
||||
if 待添加虚线.方向() == self.线段方向 {
|
||||
return Err("添加方向与线段方向相同".into());
|
||||
}
|
||||
@@ -138,14 +155,14 @@ impl 线段特征 {
|
||||
}
|
||||
|
||||
/// 从特征序列元素中删除虚线
|
||||
pub fn 删除(&mut self, 待删除虚线: &Rc<虚线>) -> Result<(), String> {
|
||||
pub fn 删除(&mut self, 待删除虚线: &Arc<虚线>) -> Result<(), String> {
|
||||
if 待删除虚线.方向() == self.方向() {
|
||||
return Err("删除方向与特征序列方向相同".into());
|
||||
}
|
||||
if let Some(pos) = self
|
||||
.元素
|
||||
.iter()
|
||||
.position(|x| Rc::as_ptr(x) == Rc::as_ptr(待删除虚线))
|
||||
.position(|x| Arc::as_ptr(x) == Arc::as_ptr(待删除虚线))
|
||||
{
|
||||
self.元素.remove(pos);
|
||||
Ok(())
|
||||
@@ -155,19 +172,19 @@ impl 线段特征 {
|
||||
}
|
||||
|
||||
/// 新建特征序列元素
|
||||
pub fn 新建(虚线序列: Vec<Rc<虚线>>, 线段方向: 相对方向) -> Self {
|
||||
pub fn 新建(虚线序列: Vec<Arc<虚线>>, 线段方向: 相对方向) -> Self {
|
||||
let 标识 = format!("特征<虚线>");
|
||||
Self::new(标识, 虚线序列, 线段方向)
|
||||
}
|
||||
|
||||
/// 静态分析 — 从虚线序列生成特征序列元素列表
|
||||
pub fn 静态分析(
|
||||
虚线序列: &[Rc<虚线>],
|
||||
虚线序列: &[Arc<虚线>],
|
||||
线段方向: 相对方向,
|
||||
四象: &str,
|
||||
是否忽视: bool,
|
||||
) -> Vec<Rc<线段特征>> {
|
||||
let mut 结果: Vec<Rc<线段特征>> = Vec::new();
|
||||
) -> Vec<Arc<线段特征>> {
|
||||
let mut 结果: Vec<Arc<线段特征>> = Vec::new();
|
||||
|
||||
// 需要被合并的方向集合
|
||||
let 需要合并: Vec<相对方向> = match 四象 {
|
||||
@@ -179,9 +196,9 @@ impl 线段特征 {
|
||||
// 情况1:方向相同(可能触发分型替换)
|
||||
if 虚线.方向() == 线段方向 {
|
||||
if 结果.len() >= 3 {
|
||||
let 左 = Rc::clone(&结果[结果.len() - 3]);
|
||||
let 中 = Rc::clone(&结果[结果.len() - 2]);
|
||||
let 右 = Rc::clone(&结果[结果.len() - 1]);
|
||||
let 左 = Arc::clone(&结果[结果.len() - 3]);
|
||||
let 中 = Arc::clone(&结果[结果.len() - 2]);
|
||||
let 右 = Arc::clone(&结果[结果.len() - 1]);
|
||||
|
||||
if let Some(结构) = 分型结构::分析(&*左, &*中, &*右, true, true) {
|
||||
let 应替换 = (线段方向 == 相对方向::向上
|
||||
@@ -192,16 +209,24 @@ impl 线段特征 {
|
||||
&& 虚线.低() < 中.低());
|
||||
|
||||
if 应替换 {
|
||||
let 小号虚线 = 中.元素.iter().min_by_key(|o| o.序号).unwrap();
|
||||
let 大号虚线 = 右.元素.iter().max_by_key(|o| o.序号).unwrap();
|
||||
let 小号虚线 = 中
|
||||
.元素
|
||||
.iter()
|
||||
.min_by_key(|o| o.序号.load(Ordering::Relaxed))
|
||||
.unwrap();
|
||||
let 大号虚线 = 右
|
||||
.元素
|
||||
.iter()
|
||||
.max_by_key(|o| o.序号.load(Ordering::Relaxed))
|
||||
.unwrap();
|
||||
let fake = 虚线::创建笔(
|
||||
Rc::clone(&小号虚线.文),
|
||||
Rc::clone(&大号虚线.武),
|
||||
Arc::clone(&小号虚线.文),
|
||||
大号虚线.武.read().unwrap().clone(),
|
||||
false,
|
||||
);
|
||||
结果.pop();
|
||||
let idx = 结果.len() - 1;
|
||||
结果[idx] = Rc::new(Self::新建(vec![Rc::new(fake)], 线段方向));
|
||||
结果[idx] = Arc::new(Self::新建(vec![Arc::new(fake)], 线段方向));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,7 +235,7 @@ impl 线段特征 {
|
||||
|
||||
// 情况2:方向不同(执行特征序列的合并/添加)
|
||||
if 结果.is_empty() {
|
||||
结果.push(Rc::new(Self::新建(vec![Rc::clone(虚线)], 线段方向)));
|
||||
结果.push(Arc::new(Self::新建(vec![Arc::clone(虚线)], 线段方向)));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -225,10 +250,10 @@ impl 线段特征 {
|
||||
)) {
|
||||
// Clone-modify-replace
|
||||
let mut 新特征 = (*结果[最后_idx]).clone();
|
||||
let _ = 新特征.添加(Rc::clone(虚线));
|
||||
结果[最后_idx] = Rc::new(新特征);
|
||||
let _ = 新特征.添加(Arc::clone(虚线));
|
||||
结果[最后_idx] = Arc::new(新特征);
|
||||
} else {
|
||||
结果.push(Rc::new(Self::新建(vec![Rc::clone(虚线)], 线段方向)));
|
||||
结果.push(Arc::new(Self::新建(vec![Arc::clone(虚线)], 线段方向)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,15 +261,15 @@ impl 线段特征 {
|
||||
}
|
||||
|
||||
/// 获取分型序列
|
||||
pub fn 获取分型序列(特征序列: &[Rc<线段特征>]) -> Vec<特征分型> {
|
||||
pub fn 获取分型序列(特征序列: &[Arc<线段特征>]) -> 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]);
|
||||
let 左 = Arc::clone(&特征序列[i - 2]);
|
||||
let 中 = Arc::clone(&特征序列[i - 1]);
|
||||
let 右 = Arc::clone(&特征序列[i]);
|
||||
|
||||
let 结构 = 分型结构::分析_对象(
|
||||
&*左 as &dyn crate::types::fractal::有高低,
|
||||
@@ -285,3 +310,273 @@ impl std::fmt::Display for 线段特征 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::kline::bar::K线;
|
||||
use crate::kline::chan_kline::缠论K线;
|
||||
use crate::structure::fractal_obj::分型;
|
||||
use crate::types::分型结构;
|
||||
|
||||
fn 辅助_创建K线(时间戳: i64, 高: f64, 低: f64, 开: f64, 收: f64) -> K线 {
|
||||
let mut k = K线::default();
|
||||
k.时间戳 = 时间戳;
|
||||
k.高 = 高;
|
||||
k.低 = 低;
|
||||
k.开盘价 = 开;
|
||||
k.收盘价 = 收;
|
||||
k
|
||||
}
|
||||
|
||||
fn 辅助_创建缠K(
|
||||
时间戳: i64,
|
||||
高: f64,
|
||||
低: f64,
|
||||
方向: 相对方向,
|
||||
结构: Option<分型结构>,
|
||||
序号: i64,
|
||||
) -> Arc<缠论K线> {
|
||||
let 普K = Arc::new(辅助_创建K线(时间戳, 高, 低, 低, 高));
|
||||
Arc::new(缠论K线::创建缠K(
|
||||
时间戳, 高, 低, 方向, 结构, 序号, 普K, None,
|
||||
))
|
||||
}
|
||||
|
||||
fn 辅助_创建顶分型(时间戳: i64, 高: f64, 低: f64, 序号: i64) -> Arc<分型> {
|
||||
let 左 = 辅助_创建缠K(
|
||||
时间戳 - 2,
|
||||
高 - 2.0,
|
||||
低 - 2.0,
|
||||
相对方向::向上,
|
||||
Some(分型结构::上),
|
||||
序号 - 2,
|
||||
);
|
||||
let 中 = 辅助_创建缠K(时间戳, 高, 低, 相对方向::向上, Some(分型结构::顶), 序号);
|
||||
let 右 = 辅助_创建缠K(
|
||||
时间戳 + 2,
|
||||
高 - 1.0,
|
||||
低 - 1.0,
|
||||
相对方向::向下,
|
||||
Some(分型结构::下),
|
||||
序号 + 2,
|
||||
);
|
||||
Arc::new(分型::new(Some(左), 中, Some(右)))
|
||||
}
|
||||
|
||||
fn 辅助_创建底分型(时间戳: i64, 高: f64, 低: f64, 序号: i64) -> Arc<分型> {
|
||||
let 左 = 辅助_创建缠K(
|
||||
时间戳 - 2,
|
||||
高 + 2.0,
|
||||
低 + 2.0,
|
||||
相对方向::向下,
|
||||
Some(分型结构::下),
|
||||
序号 - 2,
|
||||
);
|
||||
let 中 = 缠论K线::创建缠K(
|
||||
时间戳,
|
||||
高,
|
||||
低,
|
||||
相对方向::向下,
|
||||
Some(分型结构::底),
|
||||
序号,
|
||||
Arc::new(辅助_创建K线(时间戳, 高, 低, 低, 高)),
|
||||
None,
|
||||
);
|
||||
中.分型特征值.set(低);
|
||||
let 中 = Arc::new(中);
|
||||
let 右 = 辅助_创建缠K(
|
||||
时间戳 + 2,
|
||||
高 + 1.0,
|
||||
低 + 1.0,
|
||||
相对方向::向上,
|
||||
Some(分型结构::上),
|
||||
序号 + 2,
|
||||
);
|
||||
Arc::new(分型::new(Some(左), 中, Some(右)))
|
||||
}
|
||||
|
||||
fn 辅助_创建笔(
|
||||
文时间戳: i64,
|
||||
文高: f64,
|
||||
文低: f64,
|
||||
武时间戳: i64,
|
||||
武高: f64,
|
||||
武低: f64,
|
||||
) -> Arc<虚线> {
|
||||
let 顶 = 辅助_创建顶分型(文时间戳, 文高, 文低, 1);
|
||||
let 底 = 辅助_创建底分型(武时间戳, 武高, 武低, 2);
|
||||
Arc::new(虚线::创建笔(顶, 底, true))
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 文 — 取特征值最大/最小的分型
|
||||
// 特征序列元素方向与线段方向相反:
|
||||
// 向上线段 → 元素为向下笔(顶→底) → 文=顶分型 → 取max
|
||||
// 向下线段 → 元素为向上笔(底→顶) → 文=底分型 → 取min
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_文_向上线段取最大特征值分型() {
|
||||
// 向上线段,元素用向下笔(顶→底),文=顶分型
|
||||
// 笔1: 顶(特征值=100)→底(80), 文=顶(100)
|
||||
let 笔1 = 辅助_创建笔(100, 100.0, 90.0, 200, 90.0, 80.0);
|
||||
// 笔2: 顶(特征值=110)→底(90), 文=顶(110)
|
||||
let 笔2 = 辅助_创建笔(200, 110.0, 100.0, 300, 90.0, 80.0);
|
||||
|
||||
let feat = 线段特征::new(
|
||||
"测试".into(),
|
||||
vec![Arc::clone(&笔1), Arc::clone(&笔2)],
|
||||
相对方向::向上,
|
||||
);
|
||||
|
||||
// 向上取max → 笔2.文=110
|
||||
let 文 = feat.文();
|
||||
assert!((文.分型特征值 - 110.0).abs() < 0.01);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_文_向下线段取最小特征值分型() {
|
||||
// 向下线段,元素用向上笔(底→顶),文=底分型
|
||||
// 笔1: 底(特征值=80)→顶(100), 文=底(80)
|
||||
let 底1 = 辅助_创建底分型(100, 90.0, 80.0, 5);
|
||||
let 顶1 = 辅助_创建顶分型(200, 100.0, 90.0, 10);
|
||||
let 笔1 = Arc::new(虚线::创建笔(底1, 顶1, true));
|
||||
|
||||
// 笔2: 底(特征值=70)→顶(95), 文=底(70)
|
||||
let 底2 = 辅助_创建底分型(200, 80.0, 70.0, 15);
|
||||
let 顶2 = 辅助_创建顶分型(300, 95.0, 85.0, 20);
|
||||
let 笔2 = Arc::new(虚线::创建笔(底2, 顶2, true));
|
||||
|
||||
let feat = 线段特征::new(
|
||||
"测试".into(),
|
||||
vec![Arc::clone(&笔1), Arc::clone(&笔2)],
|
||||
相对方向::向下,
|
||||
);
|
||||
|
||||
// 向下取min → 笔2.文=70
|
||||
let 文 = feat.文();
|
||||
assert!((文.分型特征值 - 70.0).abs() < 0.01);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 武 — 取特征值最大/最小的分型
|
||||
// 向上线段 → 元素为向下笔 → 武=底分型 → 取max
|
||||
// 向下线段 → 元素为向上笔 → 武=顶分型 → 取min
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_武_向上线段取最大特征值分型() {
|
||||
// 向上线段,元素用向下笔(顶→底),武=底分型
|
||||
// 笔1: 顶(100)→底(特征值=80), 武=底(80)
|
||||
let 笔1 = 辅助_创建笔(100, 100.0, 90.0, 200, 90.0, 80.0);
|
||||
// 笔2: 顶(110)→底(特征值=90), 武=底(90)
|
||||
let 笔2 = 辅助_创建笔(200, 110.0, 100.0, 300, 100.0, 90.0);
|
||||
|
||||
let feat = 线段特征::new(
|
||||
"测试".into(),
|
||||
vec![Arc::clone(&笔1), Arc::clone(&笔2)],
|
||||
相对方向::向上,
|
||||
);
|
||||
|
||||
// 向上取max → 笔2.武=90
|
||||
let 武 = feat.武();
|
||||
assert!((武.分型特征值 - 90.0).abs() < 0.01);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_武_向下线段取最小特征值分型() {
|
||||
// 向下线段,元素用向上笔(底→顶),武=顶分型
|
||||
// 笔1: 底(80)→顶(特征值=100), 武=顶(100)
|
||||
let 底1 = 辅助_创建底分型(100, 90.0, 80.0, 5);
|
||||
let 顶1 = 辅助_创建顶分型(200, 100.0, 90.0, 10);
|
||||
let 笔1 = Arc::new(虚线::创建笔(底1, 顶1, true));
|
||||
|
||||
// 笔2: 底(60)→顶(特征值=85), 武=顶(85)
|
||||
let 底2 = 辅助_创建底分型(200, 70.0, 60.0, 15);
|
||||
let 顶2 = 辅助_创建顶分型(300, 85.0, 75.0, 20);
|
||||
let 笔2 = Arc::new(虚线::创建笔(底2, 顶2, true));
|
||||
|
||||
let feat = 线段特征::new(
|
||||
"测试".into(),
|
||||
vec![Arc::clone(&笔1), Arc::clone(&笔2)],
|
||||
相对方向::向下,
|
||||
);
|
||||
|
||||
// 向下取min → 笔2.武=85
|
||||
let 武 = feat.武();
|
||||
assert!((武.分型特征值 - 85.0).abs() < 0.01);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 文/武 tiebreaker — 同特征值取后时间戳
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_文_同特征值取后时间戳() {
|
||||
// 两个笔的文特征值相同=100,但时间戳不同
|
||||
let 顶1 = 辅助_创建顶分型(100, 100.0, 90.0, 5);
|
||||
let 底1 = 辅助_创建底分型(200, 90.0, 80.0, 10);
|
||||
let 笔1 = Arc::new(虚线::创建笔(顶1, 底1, true));
|
||||
|
||||
let 顶2 = 辅助_创建顶分型(300, 100.0, 90.0, 15); // 同特征值,后时间戳
|
||||
let 底2 = 辅助_创建底分型(400, 80.0, 70.0, 20);
|
||||
let 笔2 = Arc::new(虚线::创建笔(顶2, 底2, true));
|
||||
|
||||
let feat = 线段特征::new("测试".into(), vec![笔1, 笔2], 相对方向::向上);
|
||||
|
||||
let 文 = feat.文();
|
||||
// 向上取最大特征值:都是100 → tiebreaker取后时间戳 → 笔2.文(300)
|
||||
assert_eq!(文.时间戳, 300);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_武_同特征值取后时间戳_向上() {
|
||||
let 顶1 = 辅助_创建顶分型(100, 100.0, 90.0, 5);
|
||||
let 底1 = 辅助_创建底分型(200, 80.0, 70.0, 10); // 特征值80
|
||||
let 笔1 = Arc::new(虚线::创建笔(顶1, 底1, true));
|
||||
|
||||
let 顶2 = 辅助_创建顶分型(300, 80.0, 70.0, 15); // 特征值80
|
||||
let 底2 = 辅助_创建底分型(400, 80.0, 70.0, 20); // 特征值80
|
||||
let 笔2 = Arc::new(虚线::创建笔(顶2, 底2, true));
|
||||
|
||||
let feat = 线段特征::new("测试".into(), vec![笔1, 笔2], 相对方向::向上);
|
||||
|
||||
let 武 = feat.武();
|
||||
// 向上取最大特征值:都是80 → tiebreaker取后时间戳 → 笔2.武(400)
|
||||
assert_eq!(武.时间戳, 400);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 添加/删除 操作
|
||||
// ============================================================
|
||||
|
||||
#[test]
|
||||
fn test_添加方向与线段方向相反的虚线可成功() {
|
||||
// 向下笔(顶→底,方向=向下) 添加到 向上线段(方向=向上) → 方向不同, 可添加
|
||||
let 笔1 = 辅助_创建笔(100, 100.0, 90.0, 200, 90.0, 80.0);
|
||||
let mut feat = 线段特征::new("测试".into(), vec![], 相对方向::向上);
|
||||
|
||||
let result = feat.添加(Arc::clone(&笔1));
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_添加方向与线段方向相同的虚线应报错() {
|
||||
// 向下笔(顶→底,方向=向下) 添加到 向下线段(方向=向下) → 方向相同, 应报错
|
||||
let 笔1 = 辅助_创建笔(100, 100.0, 90.0, 200, 90.0, 80.0);
|
||||
let mut feat = 线段特征::new("测试".into(), vec![], 相对方向::向下);
|
||||
|
||||
let result = feat.添加(Arc::clone(&笔1));
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_空线段特征文返回第一个元素的文() {
|
||||
let 笔1 = 辅助_创建笔(100, 100.0, 90.0, 200, 90.0, 80.0);
|
||||
let feat = 线段特征::new("测试".into(), vec![Arc::clone(&笔1)], 相对方向::向上);
|
||||
|
||||
let 文 = feat.文();
|
||||
assert_eq!(Arc::as_ptr(&文), Arc::as_ptr(&笔1.文));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,10 @@ pub mod bsp_type;
|
||||
pub mod direction;
|
||||
pub mod fractal;
|
||||
pub mod gap;
|
||||
pub mod sync_f64;
|
||||
|
||||
pub use bsp_type::买卖点类型;
|
||||
pub use direction::相对方向;
|
||||
pub use fractal::分型结构;
|
||||
pub use gap::缺口;
|
||||
pub use sync_f64::SyncF64;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
/// f64 原子类型 — 基于 AtomicU64 + 位转换,API 与 `Cell<f64>` 一致。
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SyncF64(AtomicU64);
|
||||
|
||||
impl SyncF64 {
|
||||
pub fn new(v: f64) -> Self {
|
||||
Self(AtomicU64::new(v.to_bits()))
|
||||
}
|
||||
|
||||
pub fn get(&self) -> f64 {
|
||||
f64::from_bits(self.0.load(Ordering::Relaxed))
|
||||
}
|
||||
|
||||
pub fn set(&self, v: f64) {
|
||||
self.0.store(v.to_bits(), Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for SyncF64 {
|
||||
fn clone(&self) -> Self {
|
||||
Self(AtomicU64::new(self.0.load(Ordering::Relaxed)))
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/// 将 f64 格式化为与 Python `:g` (6 位有效数字) 兼容的字符串。
|
||||
pub fn format_f64_g(value: f64) -> String {
|
||||
if value.is_nan() {
|
||||
return "nan".to_string();
|
||||
@@ -33,9 +34,61 @@ pub fn format_f64_g(value: f64) -> String {
|
||||
"-inf".to_string()
|
||||
};
|
||||
}
|
||||
if value == 0.0 {
|
||||
return "0".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()
|
||||
let abs = value.abs();
|
||||
let exp = abs.log10().floor() as i32;
|
||||
|
||||
// Python :g 科学计数法边界: exp < -4 或 exp >= p (=6)
|
||||
if exp < -4 || exp >= 6 {
|
||||
let significand = value / 10_f64.powi(exp);
|
||||
let s = format!("{:.5}", significand);
|
||||
let s = s.trim_end_matches('0').trim_end_matches('.');
|
||||
return format!("{}e{:+03}", s, exp);
|
||||
}
|
||||
|
||||
// 定点表示
|
||||
if abs >= 1.0 {
|
||||
let int_digits = exp as usize + 1;
|
||||
if int_digits >= 6 {
|
||||
return format!("{:.0}", value);
|
||||
}
|
||||
let s = format!("{:.prec$}", value, prec = 6 - int_digits);
|
||||
let s = s.trim_end_matches('0');
|
||||
return s.trim_end_matches('.').to_string();
|
||||
} else {
|
||||
let leading_zeros = (-exp) as usize;
|
||||
let s = format!("{:.prec$}", value, prec = leading_zeros + 5);
|
||||
let s = s.trim_end_matches('0');
|
||||
return s.trim_end_matches('.').to_string();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_format_large() {
|
||||
assert_eq!(format_f64_g(82833.0), "82833");
|
||||
assert_eq!(format_f64_g(74192.2), "74192.2");
|
||||
assert_eq!(format_f64_g(100.0), "100");
|
||||
assert_eq!(format_f64_g(0.0), "0");
|
||||
assert_eq!(format_f64_g(12345.6789), "12345.7");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_small() {
|
||||
assert_eq!(format_f64_g(0.001234), "0.001234");
|
||||
assert_eq!(format_f64_g(0.1), "0.1");
|
||||
assert_eq!(format_f64_g(0.001), "0.001");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_extreme() {
|
||||
assert_eq!(format_f64_g(1e7), "1e+07");
|
||||
assert_eq!(format_f64_g(1e-5), "1e-05");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user