1. thread_local! 导致跨线程缓存不可见(主要问题)
kline_py.rs 中 BSP_CACHE、KLINE_IDENTITY、BAR_IDENTITY 使用了 thread_local!。主线程调用 识别买卖点() → 买卖点信息.add() 写入的是主线程的 PySet,backtrader 策略线程读取的是自己线程独立的空 PySet。 修复:将三个缓存从 thread_local! 改为全局 static + std::sync::LazyLock<RwLock<HashMap<...>>> 影响分析 这些身份缓存的影响与 KLINE_IDENTITY/BAR_IDENTITY 不同——它们只影响 Python 对象身份(is 比较),不直接影响数据内容(因为 Rust 数据通过 Arc 共享,读写都是同一份)。 具体后果: - 不同线程访问同一个 Rust Arc 会得到不同的 Python wrapper 对象 - a is b 跨线程比较返回 False,哪怕它们包装同一个底层 Rust 对象 - 每个线程维护一份独立缓存,内存浪费(不过 wrapper 很小)
This commit is contained in:
@@ -31,26 +31,28 @@ use std::sync::atomic::Ordering;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
thread_local! {
|
// 使用全局 static 而非 thread_local!,保证跨线程对象标识一致性
|
||||||
static HUB_IDENTITY: RwLock<HashMap<usize, Py<中枢Py>>> = RwLock::new(HashMap::new());
|
static HUB_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<中枢Py>>>> =
|
||||||
}
|
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||||
|
|
||||||
pub(crate) fn hub_to_py(
|
pub(crate) fn hub_to_py(
|
||||||
py: Python<'_>, inner: Arc<chanlun::algorithm::hub::中枢>
|
py: Python<'_>, inner: Arc<chanlun::algorithm::hub::中枢>
|
||||||
) -> Py<中枢Py> {
|
) -> Py<中枢Py> {
|
||||||
let key = Arc::as_ptr(&inner) as usize;
|
let key = Arc::as_ptr(&inner) as usize;
|
||||||
if let Some(cached) =
|
if let Some(cached) = HUB_IDENTITY
|
||||||
HUB_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&key)
|
||||||
|
.map(|p| p.clone_ref(py))
|
||||||
{
|
{
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
HUB_IDENTITY.with(|c| {
|
HUB_IDENTITY
|
||||||
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
|
.write()
|
||||||
});
|
.unwrap()
|
||||||
|
.retain(|_, v| v.get_refcnt(py) > 1);
|
||||||
let obj = Py::new(py, 中枢Py { inner }).unwrap();
|
let obj = Py::new(py, 中枢Py { inner }).unwrap();
|
||||||
HUB_IDENTITY.with(|c| {
|
HUB_IDENTITY.write().unwrap().insert(key, obj.clone_ref(py));
|
||||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
|
||||||
});
|
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,13 +364,13 @@ impl 笔Py {
|
|||||||
笔序列: Vec<Py<虚线Py>>,
|
笔序列: Vec<Py<虚线Py>>,
|
||||||
文: &Bound<'_, 分型Py>,
|
文: &Bound<'_, 分型Py>,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
) -> Option<虚线Py> {
|
) -> Option<Py<虚线Py>> {
|
||||||
let bi_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 笔序列
|
let bi_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||||
.iter()
|
.iter()
|
||||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||||
.collect();
|
.collect();
|
||||||
chanlun::algorithm::bi::笔::以文会友(&bi_list, &文.borrow().inner)
|
chanlun::algorithm::bi::笔::以文会友(&bi_list, &文.borrow().inner)
|
||||||
.map(|inner| 虚线Py { inner })
|
.map(|inner| dashed_to_py(py, inner))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[classmethod]
|
#[classmethod]
|
||||||
@@ -378,13 +380,13 @@ impl 笔Py {
|
|||||||
笔序列: Vec<Py<虚线Py>>,
|
笔序列: Vec<Py<虚线Py>>,
|
||||||
武: &Bound<'_, 分型Py>,
|
武: &Bound<'_, 分型Py>,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
) -> Option<虚线Py> {
|
) -> Option<Py<虚线Py>> {
|
||||||
let bi_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 笔序列
|
let bi_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||||
.iter()
|
.iter()
|
||||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||||
.collect();
|
.collect();
|
||||||
chanlun::algorithm::bi::笔::以武会友(&bi_list, &武.borrow().inner)
|
chanlun::algorithm::bi::笔::以武会友(&bi_list, &武.borrow().inner)
|
||||||
.map(|inner| 虚线Py { inner })
|
.map(|inner| dashed_to_py(py, inner))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[classmethod]
|
#[classmethod]
|
||||||
@@ -396,13 +398,13 @@ impl 笔Py {
|
|||||||
缠K: &Bound<'_, crate::kline_py::缠论K线Py>,
|
缠K: &Bound<'_, crate::kline_py::缠论K线Py>,
|
||||||
偏移: i64,
|
偏移: i64,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
) -> Option<虚线Py> {
|
) -> Option<Py<虚线Py>> {
|
||||||
let bi_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 笔序列
|
let bi_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||||
.iter()
|
.iter()
|
||||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||||
.collect();
|
.collect();
|
||||||
chanlun::algorithm::bi::笔::根据缠K找笔(&bi_list, &缠K.borrow().inner, 偏移)
|
chanlun::algorithm::bi::笔::根据缠K找笔(&bi_list, &缠K.borrow().inner, 偏移)
|
||||||
.map(|inner| 虚线Py { inner })
|
.map(|inner| dashed_to_py(py, inner))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[classmethod]
|
#[classmethod]
|
||||||
@@ -501,7 +503,7 @@ impl 笔Py {
|
|||||||
) -> bool {
|
) -> bool {
|
||||||
let obs = 观察员.borrow();
|
let obs = 观察员.borrow();
|
||||||
let obs_ref = obs.obs();
|
let obs_ref = obs.obs();
|
||||||
chanlun::algorithm::bi::笔::自检(&筆.borrow().inner, &*obs_ref)
|
chanlun::algorithm::bi::笔::自检(&筆.borrow().inner, &obs_ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[classmethod]
|
#[classmethod]
|
||||||
@@ -510,12 +512,13 @@ impl 笔Py {
|
|||||||
_cls: &Bound<'_, PyType>,
|
_cls: &Bound<'_, PyType>,
|
||||||
筆: &Bound<'_, 虚线Py>,
|
筆: &Bound<'_, 虚线Py>,
|
||||||
观察员: &Bound<'_, 观察者Py>,
|
观察员: &Bound<'_, 观察者Py>,
|
||||||
) -> Vec<虚线Py> {
|
py: Python<'_>,
|
||||||
|
) -> Vec<Py<虚线Py>> {
|
||||||
let obs = 观察员.borrow();
|
let obs = 观察员.borrow();
|
||||||
let obs_ref = obs.obs();
|
let obs_ref = obs.obs();
|
||||||
chanlun::algorithm::bi::笔::获取所有停顿位置(&筆.borrow().inner, &*obs_ref)
|
chanlun::algorithm::bi::笔::获取所有停顿位置(&筆.borrow().inner, &obs_ref)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|d| 虚线Py { inner: Arc::new(d) })
|
.map(|d| dashed_to_py(py, Arc::new(d)))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,7 +532,7 @@ impl 笔Py {
|
|||||||
) -> Vec<Py<缠论K线Py>> {
|
) -> Vec<Py<缠论K线Py>> {
|
||||||
let obs = 观察员.borrow();
|
let obs = 观察员.borrow();
|
||||||
let obs_ref = obs.obs();
|
let obs_ref = obs.obs();
|
||||||
chanlun::algorithm::bi::笔::是否背驰过(&当前筆.borrow().inner, &*obs_ref)
|
chanlun::algorithm::bi::笔::是否背驰过(&当前筆.borrow().inner, &obs_ref)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|ck| chan_kline_to_py(py, ck))
|
.map(|ck| chan_kline_to_py(py, ck))
|
||||||
.collect()
|
.collect()
|
||||||
@@ -569,8 +572,8 @@ impl 线段Py {
|
|||||||
筆: &Bound<'_, 虚线Py>,
|
筆: &Bound<'_, 虚线Py>,
|
||||||
) -> PyResult<()> {
|
) -> PyResult<()> {
|
||||||
let bi_rc = Arc::clone(&筆.borrow().inner);
|
let bi_rc = Arc::clone(&筆.borrow().inner);
|
||||||
let mut ref_mut = 段.borrow_mut();
|
let ref_mut = 段.borrow_mut();
|
||||||
chanlun::algorithm::segment::线段::添加虚线(&mut ref_mut.inner, bi_rc);
|
chanlun::algorithm::segment::线段::添加虚线(&ref_mut.inner, bi_rc);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,9 +585,9 @@ impl 线段Py {
|
|||||||
武: &Bound<'_, 分型Py>,
|
武: &Bound<'_, 分型Py>,
|
||||||
行号: u32,
|
行号: u32,
|
||||||
) -> PyResult<()> {
|
) -> PyResult<()> {
|
||||||
let mut ref_mut = 段.borrow_mut();
|
let ref_mut = 段.borrow_mut();
|
||||||
chanlun::algorithm::segment::线段::武斗(
|
chanlun::algorithm::segment::线段::武斗(
|
||||||
&mut ref_mut.inner,
|
&ref_mut.inner,
|
||||||
&Arc::clone(&武.borrow().inner),
|
&Arc::clone(&武.borrow().inner),
|
||||||
行号,
|
行号,
|
||||||
);
|
);
|
||||||
@@ -594,8 +597,8 @@ impl 线段Py {
|
|||||||
#[classmethod]
|
#[classmethod]
|
||||||
/// 武终
|
/// 武终
|
||||||
fn 武终(_cls: &Bound<'_, PyType>, 段: &Bound<'_, 虚线Py>, 行号: u32) -> PyResult<()> {
|
fn 武终(_cls: &Bound<'_, PyType>, 段: &Bound<'_, 虚线Py>, 行号: u32) -> PyResult<()> {
|
||||||
let mut ref_mut = 段.borrow_mut();
|
let ref_mut = 段.borrow_mut();
|
||||||
chanlun::algorithm::segment::线段::武终(&mut ref_mut.inner, 行号);
|
chanlun::algorithm::segment::线段::武终(&ref_mut.inner, 行号);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -607,12 +610,12 @@ impl 线段Py {
|
|||||||
序列: Vec<Py<虚线Py>>,
|
序列: Vec<Py<虚线Py>>,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
) -> PyResult<()> {
|
) -> PyResult<()> {
|
||||||
let mut ref_mut = 段.borrow_mut();
|
let ref_mut = 段.borrow_mut();
|
||||||
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 序列
|
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 序列
|
||||||
.iter()
|
.iter()
|
||||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||||
.collect();
|
.collect();
|
||||||
chanlun::algorithm::segment::线段::验证序列(&mut ref_mut.inner, &rc_list);
|
chanlun::algorithm::segment::线段::验证序列(&ref_mut.inner, &rc_list);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,12 +627,12 @@ impl 线段Py {
|
|||||||
序列: Vec<Py<虚线Py>>,
|
序列: Vec<Py<虚线Py>>,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
) -> PyResult<()> {
|
) -> PyResult<()> {
|
||||||
let mut ref_mut = 段.borrow_mut();
|
let ref_mut = 段.borrow_mut();
|
||||||
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 序列
|
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 序列
|
||||||
.iter()
|
.iter()
|
||||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||||
.collect();
|
.collect();
|
||||||
chanlun::algorithm::segment::线段::序列重置(&mut ref_mut.inner, &rc_list);
|
chanlun::algorithm::segment::线段::序列重置(&ref_mut.inner, &rc_list);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -692,7 +695,7 @@ impl 线段Py {
|
|||||||
let seq: Vec<Option<Arc<chanlun::structure::segment_feat::线段特征>>> = if 序列.is_none()
|
let seq: Vec<Option<Arc<chanlun::structure::segment_feat::线段特征>>> = if 序列.is_none()
|
||||||
{
|
{
|
||||||
vec![]
|
vec![]
|
||||||
} else if let Ok(list) = 序列.downcast::<pyo3::types::PyList>() {
|
} else if let Ok(list) = 序列.cast::<pyo3::types::PyList>() {
|
||||||
let mut result = Vec::with_capacity(list.len());
|
let mut result = Vec::with_capacity(list.len());
|
||||||
for item in list.iter() {
|
for item in list.iter() {
|
||||||
if item.is_none() {
|
if item.is_none() {
|
||||||
@@ -708,8 +711,8 @@ impl 线段Py {
|
|||||||
"序列 必须是 list 或 None",
|
"序列 必须是 list 或 None",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
let mut ref_mut = 段.borrow_mut();
|
let ref_mut = 段.borrow_mut();
|
||||||
chanlun::algorithm::segment::线段::设置特征序列(&mut ref_mut.inner, seq, 行号);
|
chanlun::algorithm::segment::线段::设置特征序列(&ref_mut.inner, seq, 行号);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -721,22 +724,26 @@ impl 线段Py {
|
|||||||
配置: &Bound<'_, 缠论配置Py>,
|
配置: &Bound<'_, 缠论配置Py>,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
) -> PyResult<()> {
|
) -> PyResult<()> {
|
||||||
let mut ref_mut = 段.borrow_mut();
|
let ref_mut = 段.borrow_mut();
|
||||||
let config = 配置.borrow().to_rust_config(py)?;
|
let config = 配置.borrow().to_rust_config(py)?;
|
||||||
chanlun::algorithm::segment::线段::刷新特征序列(&mut ref_mut.inner, &config);
|
chanlun::algorithm::segment::线段::刷新特征序列(&ref_mut.inner, &config);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[classmethod]
|
#[classmethod]
|
||||||
/// 查找贯穿伤
|
/// 查找贯穿伤
|
||||||
fn 查找贯穿伤(_cls: &Bound<'_, PyType>, 段: &Bound<'_, 虚线Py>) -> Option<虚线Py> {
|
fn 查找贯穿伤(
|
||||||
|
_cls: &Bound<'_, PyType>, 段: &Bound<'_, 虚线Py>
|
||||||
|
) -> Option<Py<虚线Py>> {
|
||||||
|
let py = _cls.py();
|
||||||
chanlun::algorithm::segment::线段::查找贯穿伤(&段.borrow().inner)
|
chanlun::algorithm::segment::线段::查找贯穿伤(&段.borrow().inner)
|
||||||
.map(|inner| 虚线Py { inner })
|
.map(|inner| dashed_to_py(py, inner))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[classmethod]
|
#[classmethod]
|
||||||
#[pyo3(signature = (段, 所属中枢 = None))]
|
#[pyo3(signature = (段, 所属中枢 = None))]
|
||||||
/// 将线段基础序列分割为前/后/第三买卖/贯穿伤四部分
|
/// 将线段基础序列分割为前/后/第三买卖/贯穿伤四部分
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn 分割序列(
|
fn 分割序列(
|
||||||
_cls: &Bound<'_, PyType>,
|
_cls: &Bound<'_, PyType>,
|
||||||
段: &Bound<'_, 虚线Py>,
|
段: &Bound<'_, 虚线Py>,
|
||||||
@@ -761,19 +768,10 @@ impl 线段Py {
|
|||||||
} else {
|
} else {
|
||||||
chanlun::algorithm::segment::线段::分割序列(&borrowed.inner, None)
|
chanlun::algorithm::segment::线段::分割序列(&borrowed.inner, None)
|
||||||
};
|
};
|
||||||
let wrap = |v: Vec<Arc<chanlun::structure::dash_line::虚线>>| -> PyResult<Vec<Py<虚线Py>>> {
|
let wrap = |v: Vec<Arc<chanlun::structure::dash_line::虚线>>| -> Vec<Py<虚线Py>> {
|
||||||
let mut result = Vec::new();
|
v.into_iter().map(|x| dashed_to_py(py, x)).collect()
|
||||||
for x in v {
|
|
||||||
result.push(Py::new(py, 虚线Py { inner: x })?);
|
|
||||||
}
|
|
||||||
Ok(result)
|
|
||||||
};
|
};
|
||||||
Ok((
|
Ok((wrap(a), wrap(b), wrap(c), d.map(|x| dashed_to_py(py, x))))
|
||||||
wrap(a)?,
|
|
||||||
wrap(b)?,
|
|
||||||
wrap(c)?,
|
|
||||||
d.map(|x| Py::new(py, 虚线Py { inner: x })).transpose()?,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[classmethod]
|
#[classmethod]
|
||||||
@@ -784,9 +782,9 @@ impl 线段Py {
|
|||||||
配置: &Bound<'_, 缠论配置Py>,
|
配置: &Bound<'_, 缠论配置Py>,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
) -> PyResult<()> {
|
) -> PyResult<()> {
|
||||||
let mut ref_mut = 段.borrow_mut();
|
let ref_mut = 段.borrow_mut();
|
||||||
let config = 配置.borrow().to_rust_config(py)?;
|
let config = 配置.borrow().to_rust_config(py)?;
|
||||||
chanlun::algorithm::segment::线段::刷新(&mut ref_mut.inner, &config);
|
chanlun::algorithm::segment::线段::刷新(&ref_mut.inner, &config);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -798,16 +796,14 @@ impl 线段Py {
|
|||||||
配置: &Bound<'_, 缠论配置Py>,
|
配置: &Bound<'_, 缠论配置Py>,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
) -> PyResult<(Py<PyAny>, Py<PyAny>, Py<PyAny>)> {
|
) -> PyResult<(Py<PyAny>, Py<PyAny>, Py<PyAny>)> {
|
||||||
let mut ref_mut = 段.borrow_mut();
|
let ref_mut = 段.borrow_mut();
|
||||||
let config = 配置.borrow().to_rust_config(py)?;
|
let config = 配置.borrow().to_rust_config(py)?;
|
||||||
let (a, b, c) = chanlun::algorithm::segment::线段::获取内部中枢序列(
|
let (a, b, c) =
|
||||||
&mut ref_mut.inner,
|
chanlun::algorithm::segment::线段::获取内部中枢序列(&ref_mut.inner, &config);
|
||||||
&config,
|
|
||||||
);
|
|
||||||
let pk_list = |v: Vec<Arc<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);
|
let list = pyo3::types::PyList::empty(py);
|
||||||
for h in v {
|
for h in v {
|
||||||
list.append(Py::new(py, 中枢Py { inner: h })?)?;
|
list.append(hub_to_py(py, h))?;
|
||||||
}
|
}
|
||||||
Ok(list.into())
|
Ok(list.into())
|
||||||
};
|
};
|
||||||
@@ -818,7 +814,7 @@ impl 线段Py {
|
|||||||
/// 内部方法:向线段序列添加新线段
|
/// 内部方法:向线段序列添加新线段
|
||||||
fn _添加线段(
|
fn _添加线段(
|
||||||
_cls: &Bound<'_, PyType>,
|
_cls: &Bound<'_, PyType>,
|
||||||
线段序列: &Bound<'_, PyAny>,
|
_线段序列: &Bound<'_, PyAny>,
|
||||||
待添加线段: &Bound<'_, 虚线Py>,
|
待添加线段: &Bound<'_, 虚线Py>,
|
||||||
配置: &Bound<'_, 缠论配置Py>,
|
配置: &Bound<'_, 缠论配置Py>,
|
||||||
行号: String,
|
行号: String,
|
||||||
@@ -839,12 +835,12 @@ impl 线段Py {
|
|||||||
/// 内部方法:从线段序列弹出最后一个线段
|
/// 内部方法:从线段序列弹出最后一个线段
|
||||||
fn _弹出线段(
|
fn _弹出线段(
|
||||||
_cls: &Bound<'_, PyType>,
|
_cls: &Bound<'_, PyType>,
|
||||||
线段序列: &Bound<'_, PyAny>,
|
_线段序列: &Bound<'_, PyAny>,
|
||||||
待弹出线段: &Bound<'_, 虚线Py>,
|
待弹出线段: &Bound<'_, 虚线Py>,
|
||||||
配置: &Bound<'_, 缠论配置Py>,
|
配置: &Bound<'_, 缠论配置Py>,
|
||||||
行号: String,
|
行号: String,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
) -> PyResult<Option<虚线Py>> {
|
) -> PyResult<Option<Py<虚线Py>>> {
|
||||||
let config = 配置.borrow().to_rust_config(py)?;
|
let config = 配置.borrow().to_rust_config(py)?;
|
||||||
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = vec![];
|
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = vec![];
|
||||||
let result = chanlun::algorithm::segment::线段::_弹出线段(
|
let result = chanlun::algorithm::segment::线段::_弹出线段(
|
||||||
@@ -853,7 +849,7 @@ impl 线段Py {
|
|||||||
&config,
|
&config,
|
||||||
行号,
|
行号,
|
||||||
);
|
);
|
||||||
Ok(result.map(|inner| 虚线Py { inner }))
|
Ok(result.map(|inner| dashed_to_py(py, inner)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[classmethod]
|
#[classmethod]
|
||||||
@@ -1007,7 +1003,7 @@ impl 线段Py {
|
|||||||
待弹出线段: &Bound<'_, 虚线Py>,
|
待弹出线段: &Bound<'_, 虚线Py>,
|
||||||
行号: u32,
|
行号: u32,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
) -> PyResult<Option<虚线Py>> {
|
) -> PyResult<Option<Py<虚线Py>>> {
|
||||||
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = 线段序列
|
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||||
.iter()
|
.iter()
|
||||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||||
@@ -1017,7 +1013,7 @@ impl 线段Py {
|
|||||||
&Arc::clone(&待弹出线段.borrow().inner),
|
&Arc::clone(&待弹出线段.borrow().inner),
|
||||||
行号,
|
行号,
|
||||||
);
|
);
|
||||||
Ok(result.map(|inner| 虚线Py { inner }))
|
Ok(result.map(|inner| dashed_to_py(py, inner)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[classmethod]
|
#[classmethod]
|
||||||
@@ -1053,7 +1049,7 @@ impl 线段Py {
|
|||||||
let obs_ref = obs.obs();
|
let obs_ref = obs.obs();
|
||||||
chanlun::algorithm::segment::线段::判断线段内部是否背驰(
|
chanlun::algorithm::segment::线段::判断线段内部是否背驰(
|
||||||
&当前段.borrow().inner,
|
&当前段.borrow().inner,
|
||||||
&*obs_ref,
|
&obs_ref,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1063,12 +1059,13 @@ impl 线段Py {
|
|||||||
_cls: &Bound<'_, PyType>,
|
_cls: &Bound<'_, PyType>,
|
||||||
段: &Bound<'_, 虚线Py>,
|
段: &Bound<'_, 虚线Py>,
|
||||||
观察员: &Bound<'_, 观察者Py>,
|
观察员: &Bound<'_, 观察者Py>,
|
||||||
) -> Vec<虚线Py> {
|
py: Python<'_>,
|
||||||
|
) -> Vec<Py<虚线Py>> {
|
||||||
let obs = 观察员.borrow();
|
let obs = 观察员.borrow();
|
||||||
let obs_ref = obs.obs();
|
let obs_ref = obs.obs();
|
||||||
chanlun::algorithm::segment::线段::获取所有停顿位置(&段.borrow().inner, &*obs_ref)
|
chanlun::algorithm::segment::线段::获取所有停顿位置(&段.borrow().inner, &obs_ref)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|d| 虚线Py { inner: Arc::new(d) })
|
.map(|d| dashed_to_py(py, Arc::new(d)))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1082,7 +1079,7 @@ impl 线段Py {
|
|||||||
) -> Vec<Py<缠论K线Py>> {
|
) -> Vec<Py<缠论K线Py>> {
|
||||||
let obs = 观察员.borrow();
|
let obs = 观察员.borrow();
|
||||||
let obs_ref = obs.obs();
|
let obs_ref = obs.obs();
|
||||||
chanlun::algorithm::segment::线段::是否背驰过(&当前段.borrow().inner, &*obs_ref)
|
chanlun::algorithm::segment::线段::是否背驰过(&当前段.borrow().inner, &obs_ref)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|ck| chan_kline_to_py(py, ck))
|
.map(|ck| chan_kline_to_py(py, ck))
|
||||||
.collect()
|
.collect()
|
||||||
@@ -1264,7 +1261,7 @@ impl 中枢Py {
|
|||||||
fn 获取序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
fn 获取序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||||
let list = pyo3::types::PyList::empty(py);
|
let list = pyo3::types::PyList::empty(py);
|
||||||
for d in self.inner.获取序列() {
|
for d in self.inner.获取序列() {
|
||||||
list.append(虚线Py { inner: d })?;
|
list.append(dashed_to_py(py, d))?;
|
||||||
}
|
}
|
||||||
Ok(list.into())
|
Ok(list.into())
|
||||||
}
|
}
|
||||||
@@ -1381,16 +1378,15 @@ impl 中枢Py {
|
|||||||
右: &Bound<'_, 虚线Py>,
|
右: &Bound<'_, 虚线Py>,
|
||||||
级别: i64,
|
级别: i64,
|
||||||
标识: &str,
|
标识: &str,
|
||||||
) -> Self {
|
) -> Py<中枢Py> {
|
||||||
Self {
|
let inner = Arc::new(chanlun::algorithm::hub::中枢::创建(
|
||||||
inner: Arc::new(chanlun::algorithm::hub::中枢::创建(
|
Arc::clone(&左.borrow().inner),
|
||||||
Arc::clone(&左.borrow().inner),
|
Arc::clone(&中.borrow().inner),
|
||||||
Arc::clone(&中.borrow().inner),
|
Arc::clone(&右.borrow().inner),
|
||||||
Arc::clone(&右.borrow().inner),
|
级别,
|
||||||
级别,
|
标识,
|
||||||
标识,
|
));
|
||||||
)),
|
hub_to_py(_cls.py(), inner)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[classmethod]
|
#[classmethod]
|
||||||
@@ -1401,7 +1397,7 @@ impl 中枢Py {
|
|||||||
起始方向: &Bound<'_, 相对方向Py>,
|
起始方向: &Bound<'_, 相对方向Py>,
|
||||||
标识: &str,
|
标识: &str,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
) -> Option<Self> {
|
) -> Option<Py<中枢Py>> {
|
||||||
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 虚线序列
|
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 虚线序列
|
||||||
.iter()
|
.iter()
|
||||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||||
@@ -1411,7 +1407,7 @@ impl 中枢Py {
|
|||||||
起始方向.borrow().inner,
|
起始方向.borrow().inner,
|
||||||
标识,
|
标识,
|
||||||
)
|
)
|
||||||
.map(|inner| Self { inner })
|
.map(|inner| hub_to_py(py, inner))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[classmethod]
|
#[classmethod]
|
||||||
@@ -1421,8 +1417,9 @@ impl 中枢Py {
|
|||||||
中枢序列: &Bound<'_, PyAny>,
|
中枢序列: &Bound<'_, PyAny>,
|
||||||
待添加中枢: &Bound<'_, Self>,
|
待添加中枢: &Bound<'_, Self>,
|
||||||
) -> PyResult<()> {
|
) -> PyResult<()> {
|
||||||
|
let py = 中枢序列.py();
|
||||||
let inner = Arc::clone(&待添加中枢.borrow().inner);
|
let inner = Arc::clone(&待添加中枢.borrow().inner);
|
||||||
let wrapper = Py::new(中枢序列.py(), Self { inner })?;
|
let wrapper = hub_to_py(py, inner);
|
||||||
中枢序列.call_method1("append", (wrapper,))?;
|
中枢序列.call_method1("append", (wrapper,))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -1432,7 +1429,7 @@ impl 中枢Py {
|
|||||||
fn 从中枢序列尾部弹出(
|
fn 从中枢序列尾部弹出(
|
||||||
_cls: &Bound<'_, PyType>,
|
_cls: &Bound<'_, PyType>,
|
||||||
中枢序列: &Bound<'_, PyAny>,
|
中枢序列: &Bound<'_, PyAny>,
|
||||||
待弹出中枢: &Bound<'_, Self>,
|
_待弹出中枢: &Bound<'_, Self>,
|
||||||
) -> PyResult<Option<Self>> {
|
) -> PyResult<Option<Self>> {
|
||||||
let result = 中枢序列.call_method1("pop", ())?;
|
let result = 中枢序列.call_method1("pop", ())?;
|
||||||
if result.is_none() {
|
if result.is_none() {
|
||||||
|
|||||||
@@ -28,14 +28,12 @@ use std::sync::RwLock;
|
|||||||
|
|
||||||
use crate::algorithm_py::hub_to_py;
|
use crate::algorithm_py::hub_to_py;
|
||||||
use crate::kline_py::bar_to_py;
|
use crate::kline_py::bar_to_py;
|
||||||
use crate::structure_py::{dashed_to_py, fractal_to_py};
|
use crate::structure_py::{dashed_to_py, fractal_to_py, 分型Py};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::algorithm_py::中枢Py;
|
|
||||||
use crate::config_py::缠论配置Py;
|
use crate::config_py::缠论配置Py;
|
||||||
use crate::kline_py::{缠论K线Py, K线Py};
|
use crate::kline_py::{缠论K线Py, K线Py};
|
||||||
use crate::structure_py::{分型Py, 虚线Py};
|
|
||||||
use crate::types_py::买卖点类型Py;
|
use crate::types_py::买卖点类型Py;
|
||||||
|
|
||||||
// ========== 基础买卖点 ==========
|
// ========== 基础买卖点 ==========
|
||||||
@@ -100,10 +98,8 @@ impl 基础买卖点Py {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[getter]
|
#[getter]
|
||||||
fn 买卖点分型(&self) -> 分型Py {
|
fn 买卖点分型(&self, py: Python<'_>) -> Py<分型Py> {
|
||||||
分型Py {
|
fractal_to_py(py, Arc::clone(&self.inner.买卖点分型))
|
||||||
inner: Arc::clone(&self.inner.买卖点分型),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[getter]
|
#[getter]
|
||||||
@@ -576,13 +572,13 @@ impl 观察者Py {
|
|||||||
let obs = me.obs();
|
let obs = me.obs();
|
||||||
(obs.符号.clone(), obs.周期)
|
(obs.符号.clone(), obs.周期)
|
||||||
};
|
};
|
||||||
let kline = K线Py {
|
let kline = bar_to_py(
|
||||||
inner: Arc::new(chanlun::kline::bar::K线::创建普K(
|
slf.py(),
|
||||||
|
Arc::new(chanlun::kline::bar::K线::创建普K(
|
||||||
&符号, 时间戳, 开, 高, 低, 收, 量, 0, 周期,
|
&符号, 时间戳, 开, 高, 低, 收, 量, 0, 周期,
|
||||||
)),
|
)),
|
||||||
};
|
);
|
||||||
let kline_py = Py::new(slf.py(), kline)?;
|
slf.call_method1("增加原始K线", (kline,))?;
|
||||||
slf.call_method1("增加原始K线", (kline_py,))?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -860,11 +856,11 @@ impl K线合成器Py {
|
|||||||
&mut self,
|
&mut self,
|
||||||
普K: &Bound<'_, K线Py>,
|
普K: &Bound<'_, K线Py>,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
) -> PyResult<Vec<(i64, K线Py)>> {
|
) -> PyResult<Vec<(i64, Py<K线Py>)>> {
|
||||||
let results = self.inner.投喂K线((*普K.borrow().inner).clone());
|
let results = self.inner.投喂K线((*普K.borrow().inner).clone());
|
||||||
Ok(results
|
Ok(results
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(周期, k)| (周期, K线Py { inner: Arc::new(k) }))
|
.map(|(周期, k)| (周期, bar_to_py(py, Arc::new(k))))
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -877,7 +873,8 @@ impl K线合成器Py {
|
|||||||
低: f64,
|
低: f64,
|
||||||
收: f64,
|
收: f64,
|
||||||
量: f64,
|
量: f64,
|
||||||
) -> Vec<(i64, K线Py)> {
|
py: Python<'_>,
|
||||||
|
) -> Vec<(i64, Py<K线Py>)> {
|
||||||
let min_cycle = self.inner.周期组.iter().copied().min().unwrap_or(1);
|
let min_cycle = self.inner.周期组.iter().copied().min().unwrap_or(1);
|
||||||
let k = chanlun::kline::bar::K线::创建普K(
|
let k = chanlun::kline::bar::K线::创建普K(
|
||||||
&self.inner.标识,
|
&self.inner.标识,
|
||||||
@@ -893,22 +890,15 @@ impl K线合成器Py {
|
|||||||
let results = self.inner.投喂K线(k);
|
let results = self.inner.投喂K线(k);
|
||||||
results
|
results
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(周期, k2)| {
|
.map(|(周期, k2)| (周期, bar_to_py(py, Arc::new(k2))))
|
||||||
(
|
|
||||||
周期,
|
|
||||||
K线Py {
|
|
||||||
inner: Arc::new(k2),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取指定周期当前正在合成的K线
|
/// 获取指定周期当前正在合成的K线
|
||||||
fn 获取当前K线(&self, 周期: i64) -> Option<K线Py> {
|
fn 获取当前K线(&self, 周期: i64, py: Python<'_>) -> Option<Py<K线Py>> {
|
||||||
self.inner.获取当前K线(周期).map(|k| K线Py {
|
self.inner
|
||||||
inner: Arc::new(k.clone()),
|
.获取当前K线(周期)
|
||||||
})
|
.map(|k| bar_to_py(py, Arc::new(k.clone())))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[getter]
|
#[getter]
|
||||||
@@ -957,7 +947,7 @@ impl 立体分析器Py {
|
|||||||
};
|
};
|
||||||
let cfg_map: Option<HashMap<i64, chanlun::config::缠论配置>> = match 配置组 {
|
let cfg_map: Option<HashMap<i64, chanlun::config::缠论配置>> = match 配置组 {
|
||||||
Some(dict_any) => {
|
Some(dict_any) => {
|
||||||
let dict = dict_any.downcast::<pyo3::types::PyDict>()?;
|
let dict = dict_any.cast::<pyo3::types::PyDict>()?;
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
for (key, value) in dict.iter() {
|
for (key, value) in dict.iter() {
|
||||||
let period: i64 = key.extract()?;
|
let period: i64 = key.extract()?;
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ impl 缠论配置Py {
|
|||||||
) -> PyResult<Py<PyDict>> {
|
) -> PyResult<Py<PyDict>> {
|
||||||
let py = 原始字典.py();
|
let py = 原始字典.py();
|
||||||
let result = PyDict::new(py);
|
let result = PyDict::new(py);
|
||||||
if let Ok(default_dict) = 默认配置.downcast::<PyDict>() {
|
if let Ok(default_dict) = 默认配置.cast::<PyDict>() {
|
||||||
for (key, value) in default_dict.iter() {
|
for (key, value) in default_dict.iter() {
|
||||||
if 原始字典.contains(&key)? {
|
if 原始字典.contains(&key)? {
|
||||||
result.set_item(key.clone(), 原始字典.get_item(&key)?)?;
|
result.set_item(key.clone(), 原始字典.get_item(&key)?)?;
|
||||||
@@ -252,6 +252,7 @@ impl 缠论配置Py {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 比较当前配置与另一个配置的差异
|
/// 比较当前配置与另一个配置的差异
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn 对比(
|
fn 对比(
|
||||||
&self,
|
&self,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
@@ -290,7 +291,10 @@ impl 缠论配置Py {
|
|||||||
Ok(Self { fields })
|
Ok(Self { fields })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn to_rust_config(&self, py: Python<'_>) -> PyResult<chanlun::config::缠论配置> {
|
pub(crate) fn to_rust_config(
|
||||||
|
&self,
|
||||||
|
_py: Python<'_>,
|
||||||
|
) -> PyResult<chanlun::config::缠论配置> {
|
||||||
dict_to_rust_config(&self.fields)
|
dict_to_rust_config(&self.fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+40
-31
@@ -355,17 +355,18 @@ impl 缠论K线Py {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_local! {
|
/// 对象标识缓存:Arc 地址 → 规范 Python 对象
|
||||||
/// 对象标识缓存:Rc 地址 → 规范 Python 对象
|
/// 确保同一底层 Arc 指针在 Python 侧始终映射到同一 PyObject
|
||||||
/// 确保同一底层 Rc 指针在 Python 侧始终映射到同一 PyObject
|
/// 使用全局 static 而非 thread_local!,保证跨线程对象标识和买卖点信息一致性
|
||||||
|
static BAR_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<K线Py>>>> =
|
||||||
|
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||||
|
|
||||||
static BAR_IDENTITY: RwLock<HashMap<usize, Py<K线Py>>> = RwLock::new(HashMap::new());
|
static KLINE_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<缠论K线Py>>>> =
|
||||||
|
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||||
|
|
||||||
static KLINE_IDENTITY: RwLock<HashMap<usize, Py<缠论K线Py>>> = RwLock::new(HashMap::new());
|
/// 买卖点信息缓存 — 按 Arc 指针全局共享,确保所有 wrapper 看到同一 PySet
|
||||||
|
static BSP_CACHE: std::sync::LazyLock<RwLock<HashMap<usize, Py<pyo3::types::PySet>>>> =
|
||||||
/// 买卖点信息缓存 — 按 Arc 指针全局共享,确保所有 wrapper 看到同一 PySet
|
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||||
static BSP_CACHE: RwLock<HashMap<usize, Py<pyo3::types::PySet>>> = RwLock::new(HashMap::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 将 Rc<K线> 转为 Py<K线Py>,确保同一 Rc 地址总是返回同一 Python 对象
|
/// 将 Rc<K线> 转为 Py<K线Py>,确保同一 Rc 地址总是返回同一 Python 对象
|
||||||
pub(crate) fn bar_to_py(
|
pub(crate) fn bar_to_py(
|
||||||
@@ -373,15 +374,16 @@ pub(crate) fn bar_to_py(
|
|||||||
inner: std::sync::Arc<chanlun::kline::bar::K线>,
|
inner: std::sync::Arc<chanlun::kline::bar::K线>,
|
||||||
) -> Py<K线Py> {
|
) -> Py<K线Py> {
|
||||||
let key = Arc::as_ptr(&inner) as usize;
|
let key = Arc::as_ptr(&inner) as usize;
|
||||||
if let Some(cached) =
|
if let Some(cached) = BAR_IDENTITY
|
||||||
BAR_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&key)
|
||||||
|
.map(|p| p.clone_ref(py))
|
||||||
{
|
{
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
let obj = Py::new(py, K线Py { inner }).unwrap();
|
let obj = Py::new(py, K线Py { inner }).unwrap();
|
||||||
BAR_IDENTITY.with(|c| {
|
BAR_IDENTITY.write().unwrap().insert(key, obj.clone_ref(py));
|
||||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
|
||||||
});
|
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,15 +393,19 @@ pub(crate) fn chan_kline_to_py(
|
|||||||
inner: std::sync::Arc<chanlun::kline::chan_kline::缠论K线>,
|
inner: std::sync::Arc<chanlun::kline::chan_kline::缠论K线>,
|
||||||
) -> Py<缠论K线Py> {
|
) -> Py<缠论K线Py> {
|
||||||
let key = Arc::as_ptr(&inner) as usize;
|
let key = Arc::as_ptr(&inner) as usize;
|
||||||
if let Some(cached) =
|
if let Some(cached) = KLINE_IDENTITY
|
||||||
KLINE_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&key)
|
||||||
|
.map(|p| p.clone_ref(py))
|
||||||
{
|
{
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
let obj = Py::new(py, 缠论K线Py::from_rc(inner)).unwrap();
|
let obj = Py::new(py, 缠论K线Py::from_rc(inner)).unwrap();
|
||||||
KLINE_IDENTITY.with(|c| {
|
KLINE_IDENTITY
|
||||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
.write()
|
||||||
});
|
.unwrap()
|
||||||
|
.insert(key, obj.clone_ref(py));
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,17 +540,18 @@ impl 缠论K线Py {
|
|||||||
// 复制买卖点信息到镜像
|
// 复制买卖点信息到镜像
|
||||||
let src_key = Arc::as_ptr(&self.inner) as usize;
|
let src_key = Arc::as_ptr(&self.inner) as usize;
|
||||||
let dst_key = Arc::as_ptr(&mirror.inner) as usize;
|
let dst_key = Arc::as_ptr(&mirror.inner) as usize;
|
||||||
let cached_src =
|
let cached_src = BSP_CACHE
|
||||||
BSP_CACHE.with(|c| c.read().unwrap().get(&src_key).map(|p| p.clone_ref(py)));
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&src_key)
|
||||||
|
.map(|p| p.clone_ref(py));
|
||||||
if let Some(cached_src) = cached_src {
|
if let Some(cached_src) = cached_src {
|
||||||
if let Ok(new_set) = pyo3::types::PySet::empty(py) {
|
if let Ok(new_set) = pyo3::types::PySet::empty(py) {
|
||||||
for item in cached_src.bind(py).iter() {
|
for item in cached_src.bind(py).iter() {
|
||||||
let _ = new_set.add(item);
|
let _ = new_set.add(item);
|
||||||
}
|
}
|
||||||
let py_set: Py<pyo3::types::PySet> = new_set.into();
|
let py_set: Py<pyo3::types::PySet> = new_set.into();
|
||||||
BSP_CACHE.with(|c| {
|
BSP_CACHE.write().unwrap().insert(dst_key, py_set);
|
||||||
c.write().unwrap().insert(dst_key, py_set);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mirror
|
mirror
|
||||||
@@ -572,18 +579,20 @@ impl 缠论K线Py {
|
|||||||
fn 买卖点信息(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
fn 买卖点信息(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||||
let key = Arc::as_ptr(&self.inner) as usize;
|
let key = Arc::as_ptr(&self.inner) as usize;
|
||||||
// 检查全局缓存
|
// 检查全局缓存
|
||||||
let cached = BSP_CACHE.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)));
|
let cached = BSP_CACHE.read().unwrap().get(&key).map(|p| p.clone_ref(py));
|
||||||
if let Some(set) = cached {
|
if let Some(set) = cached {
|
||||||
return Ok(set.into_any());
|
return Ok(set.into_any());
|
||||||
}
|
}
|
||||||
// 创建新的 PySet 并存入全局缓存
|
// 创建新的 PySet 并存入全局缓存
|
||||||
let set = pyo3::types::PySet::empty(py)?;
|
let set = pyo3::types::PySet::empty(py)?;
|
||||||
BSP_CACHE.with(|c| {
|
BSP_CACHE.write().unwrap().insert(key, set.into());
|
||||||
c.write().unwrap().insert(key, set.into());
|
|
||||||
});
|
|
||||||
// 重新读取并返回(无法从 insert 获取 Py 引用,需要重新读)
|
// 重新读取并返回(无法从 insert 获取 Py 引用,需要重新读)
|
||||||
Ok(BSP_CACHE
|
Ok(BSP_CACHE
|
||||||
.with(|c| c.read().unwrap().get(&key).unwrap().clone_ref(py))
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&key)
|
||||||
|
.unwrap()
|
||||||
|
.clone_ref(py)
|
||||||
.into_any())
|
.into_any())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -642,13 +651,13 @@ impl 缠论K线Py {
|
|||||||
配置: &Bound<'_, 缠论配置Py>,
|
配置: &Bound<'_, 缠论配置Py>,
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
) -> PyResult<(Option<Py<Self>>, Option<String>)> {
|
) -> PyResult<(Option<Py<Self>>, Option<String>)> {
|
||||||
let mut ck_inner = (*当前缠K.borrow().inner).clone();
|
let ck_inner = (*当前缠K.borrow().inner).clone();
|
||||||
let config = 配置.borrow().to_rust_config(py)?;
|
let config = 配置.borrow().to_rust_config(py)?;
|
||||||
let prev_ref = 之前缠K.map(|prev| prev.borrow());
|
let prev_ref = 之前缠K.map(|prev| prev.borrow());
|
||||||
let prev_inner = prev_ref.as_ref().map(|r| r.inner.as_ref());
|
let prev_inner = prev_ref.as_ref().map(|r| r.inner.as_ref());
|
||||||
let (result, mode) = chanlun::kline::chan_kline::缠论K线::兼并(
|
let (result, mode) = chanlun::kline::chan_kline::缠论K线::兼并(
|
||||||
prev_inner,
|
prev_inner,
|
||||||
&mut ck_inner,
|
&ck_inner,
|
||||||
&当前普K.borrow().inner,
|
&当前普K.borrow().inner,
|
||||||
&config,
|
&config,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,6 +22,8 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#![allow(non_snake_case, clippy::too_many_arguments)]
|
||||||
|
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
|||||||
@@ -29,37 +29,45 @@ use std::sync::atomic::Ordering;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
use crate::algorithm_py::{hub_to_py, 中枢Py};
|
use crate::algorithm_py::hub_to_py;
|
||||||
use crate::config_py::缠论配置Py;
|
use crate::config_py::缠论配置Py;
|
||||||
use crate::kline_py::{缠论K线Py, K线Py};
|
use crate::kline_py::{bar_to_py, 缠论K线Py, K线Py};
|
||||||
|
|
||||||
// ---- 身份缓存 (弱引用:通过 refcnt 检测存活,仅缓存持有则视为过期) ----
|
// ---- 身份缓存 (弱引用:通过 refcnt 检测存活,仅缓存持有则视为过期) ----
|
||||||
|
|
||||||
thread_local! {
|
// 使用全局 static 而非 thread_local!,保证跨线程对象标识一致性
|
||||||
static FRACTAL_IDENTITY: RwLock<HashMap<usize, Py<分型Py>>> = RwLock::new(HashMap::new());
|
static FRACTAL_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<分型Py>>>> =
|
||||||
static DASHED_IDENTITY: RwLock<HashMap<usize, Py<虚线Py>>> = RwLock::new(HashMap::new());
|
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||||
static SEGFEAT_IDENTITY: RwLock<HashMap<usize, Py<线段特征Py>>> = RwLock::new(HashMap::new());
|
static DASHED_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<虚线Py>>>> =
|
||||||
static FEATFRAC_IDENTITY: RwLock<HashMap<usize, Py<特征分型Py>>> = RwLock::new(HashMap::new());
|
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||||
}
|
static SEGFEAT_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<线段特征Py>>>> =
|
||||||
|
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||||
|
static FEATFRAC_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<特征分型Py>>>> =
|
||||||
|
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||||
|
|
||||||
pub(crate) fn fractal_to_py(
|
pub(crate) fn fractal_to_py(
|
||||||
py: Python<'_>,
|
py: Python<'_>,
|
||||||
inner: Arc<chanlun::structure::fractal_obj::分型>,
|
inner: Arc<chanlun::structure::fractal_obj::分型>,
|
||||||
) -> Py<分型Py> {
|
) -> Py<分型Py> {
|
||||||
let key = Arc::as_ptr(&inner) as usize;
|
let key = Arc::as_ptr(&inner) as usize;
|
||||||
if let Some(cached) =
|
if let Some(cached) = FRACTAL_IDENTITY
|
||||||
FRACTAL_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&key)
|
||||||
|
.map(|p| p.clone_ref(py))
|
||||||
{
|
{
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
// 清理 refcnt==1 的过期条目(仅缓存持有,Python 侧已无引用)
|
// 清理 refcnt==1 的过期条目(仅缓存持有,Python 侧已无引用)
|
||||||
FRACTAL_IDENTITY.with(|c| {
|
FRACTAL_IDENTITY
|
||||||
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
|
.write()
|
||||||
});
|
.unwrap()
|
||||||
|
.retain(|_, v| v.get_refcnt(py) > 1);
|
||||||
let obj = Py::new(py, 分型Py { inner }).unwrap();
|
let obj = Py::new(py, 分型Py { inner }).unwrap();
|
||||||
FRACTAL_IDENTITY.with(|c| {
|
FRACTAL_IDENTITY
|
||||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
.write()
|
||||||
});
|
.unwrap()
|
||||||
|
.insert(key, obj.clone_ref(py));
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,18 +76,23 @@ pub(crate) fn dashed_to_py(
|
|||||||
inner: Arc<chanlun::structure::dash_line::虚线>,
|
inner: Arc<chanlun::structure::dash_line::虚线>,
|
||||||
) -> Py<虚线Py> {
|
) -> Py<虚线Py> {
|
||||||
let key = Arc::as_ptr(&inner) as usize;
|
let key = Arc::as_ptr(&inner) as usize;
|
||||||
if let Some(cached) =
|
if let Some(cached) = DASHED_IDENTITY
|
||||||
DASHED_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&key)
|
||||||
|
.map(|p| p.clone_ref(py))
|
||||||
{
|
{
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
DASHED_IDENTITY.with(|c| {
|
DASHED_IDENTITY
|
||||||
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
|
.write()
|
||||||
});
|
.unwrap()
|
||||||
|
.retain(|_, v| v.get_refcnt(py) > 1);
|
||||||
let obj = Py::new(py, 虚线Py { inner }).unwrap();
|
let obj = Py::new(py, 虚线Py { inner }).unwrap();
|
||||||
DASHED_IDENTITY.with(|c| {
|
DASHED_IDENTITY
|
||||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
.write()
|
||||||
});
|
.unwrap()
|
||||||
|
.insert(key, obj.clone_ref(py));
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,18 +101,23 @@ pub(crate) fn segfeat_to_py(
|
|||||||
inner: Arc<chanlun::structure::segment_feat::线段特征>,
|
inner: Arc<chanlun::structure::segment_feat::线段特征>,
|
||||||
) -> Py<线段特征Py> {
|
) -> Py<线段特征Py> {
|
||||||
let key = Arc::as_ptr(&inner) as usize;
|
let key = Arc::as_ptr(&inner) as usize;
|
||||||
if let Some(cached) =
|
if let Some(cached) = SEGFEAT_IDENTITY
|
||||||
SEGFEAT_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&key)
|
||||||
|
.map(|p| p.clone_ref(py))
|
||||||
{
|
{
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
SEGFEAT_IDENTITY.with(|c| {
|
SEGFEAT_IDENTITY
|
||||||
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
|
.write()
|
||||||
});
|
.unwrap()
|
||||||
|
.retain(|_, v| v.get_refcnt(py) > 1);
|
||||||
let obj = Py::new(py, 线段特征Py { inner }).unwrap();
|
let obj = Py::new(py, 线段特征Py { inner }).unwrap();
|
||||||
SEGFEAT_IDENTITY.with(|c| {
|
SEGFEAT_IDENTITY
|
||||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
.write()
|
||||||
});
|
.unwrap()
|
||||||
|
.insert(key, obj.clone_ref(py));
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,18 +126,23 @@ pub(crate) fn featfrac_to_py(
|
|||||||
inner: Arc<chanlun::structure::feat_fractal::特征分型>,
|
inner: Arc<chanlun::structure::feat_fractal::特征分型>,
|
||||||
) -> Py<特征分型Py> {
|
) -> Py<特征分型Py> {
|
||||||
let key = Arc::as_ptr(&inner) as usize;
|
let key = Arc::as_ptr(&inner) as usize;
|
||||||
if let Some(cached) =
|
if let Some(cached) = FEATFRAC_IDENTITY
|
||||||
FEATFRAC_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get(&key)
|
||||||
|
.map(|p| p.clone_ref(py))
|
||||||
{
|
{
|
||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
FEATFRAC_IDENTITY.with(|c| {
|
FEATFRAC_IDENTITY
|
||||||
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
|
.write()
|
||||||
});
|
.unwrap()
|
||||||
|
.retain(|_, v| v.get_refcnt(py) > 1);
|
||||||
let obj = Py::new(py, 特征分型Py { inner }).unwrap();
|
let obj = Py::new(py, 特征分型Py { inner }).unwrap();
|
||||||
FEATFRAC_IDENTITY.with(|c| {
|
FEATFRAC_IDENTITY
|
||||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
.write()
|
||||||
});
|
.unwrap()
|
||||||
|
.insert(key, obj.clone_ref(py));
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
use crate::types_py::{分型结构Py, 相对方向Py, 缺口Py};
|
use crate::types_py::{分型结构Py, 相对方向Py, 缺口Py};
|
||||||
@@ -469,15 +492,13 @@ impl 虚线Py {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[getter]
|
#[getter]
|
||||||
fn 前一结束位置(&self) -> Option<Self> {
|
fn 前一结束位置(&self, py: Python<'_>) -> Option<Py<虚线Py>> {
|
||||||
self.inner
|
self.inner
|
||||||
.前一结束位置
|
.前一结束位置
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|d| Self {
|
.map(|d| dashed_to_py(py, Arc::clone(d)))
|
||||||
inner: Arc::clone(d),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 序列 getters ----
|
// ---- 序列 getters ----
|
||||||
@@ -486,12 +507,7 @@ impl 虚线Py {
|
|||||||
fn 基础序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
fn 基础序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||||
let list = pyo3::types::PyList::empty(py);
|
let list = pyo3::types::PyList::empty(py);
|
||||||
for d in self.inner.基础序列.read().unwrap().iter() {
|
for d in self.inner.基础序列.read().unwrap().iter() {
|
||||||
list.append(Py::new(
|
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||||||
py,
|
|
||||||
Self {
|
|
||||||
inner: Arc::clone(d),
|
|
||||||
},
|
|
||||||
)?)?;
|
|
||||||
}
|
}
|
||||||
Ok(list.into())
|
Ok(list.into())
|
||||||
}
|
}
|
||||||
@@ -530,12 +546,7 @@ impl 虚线Py {
|
|||||||
fn 笔序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
fn 笔序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||||
let list = pyo3::types::PyList::empty(py);
|
let list = pyo3::types::PyList::empty(py);
|
||||||
for d in self.inner.基础序列.read().unwrap().iter() {
|
for d in self.inner.基础序列.read().unwrap().iter() {
|
||||||
list.append(Py::new(
|
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||||||
py,
|
|
||||||
Self {
|
|
||||||
inner: Arc::clone(d),
|
|
||||||
},
|
|
||||||
)?)?;
|
|
||||||
}
|
}
|
||||||
Ok(list.into())
|
Ok(list.into())
|
||||||
}
|
}
|
||||||
@@ -582,11 +593,10 @@ impl 虚线Py {
|
|||||||
let obs_ref = 观察员.borrow();
|
let obs_ref = 观察员.borrow();
|
||||||
let observer_inner = obs_ref.obs();
|
let observer_inner = obs_ref.obs();
|
||||||
let result = self.inner.获取普K序列(&observer_inner.普通K线序列);
|
let result = self.inner.获取普K序列(&observer_inner.普通K线序列);
|
||||||
let list = pyo3::types::PyList::empty(观察员.py());
|
let py = 观察员.py();
|
||||||
|
let list = pyo3::types::PyList::empty(py);
|
||||||
for k in &result {
|
for k in &result {
|
||||||
list.append(K线Py {
|
list.append(bar_to_py(py, Arc::clone(k)))?;
|
||||||
inner: Arc::clone(k),
|
|
||||||
})?;
|
|
||||||
}
|
}
|
||||||
Ok(list.into())
|
Ok(list.into())
|
||||||
}
|
}
|
||||||
@@ -973,7 +983,7 @@ impl 虚线Py {
|
|||||||
) -> (bool, String) {
|
) -> (bool, String) {
|
||||||
let obs = 观察员.borrow();
|
let obs = 观察员.borrow();
|
||||||
let obs_ref = obs.obs();
|
let obs_ref = obs.obs();
|
||||||
chanlun::structure::dash_line::虚线::买卖意义(&实线.borrow().inner, &*obs_ref)
|
chanlun::structure::dash_line::虚线::买卖意义(&实线.borrow().inner, &obs_ref)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1148,7 +1158,7 @@ impl 线段特征Py {
|
|||||||
let inner = Arc::make_mut(&mut self.inner);
|
let inner = Arc::make_mut(&mut self.inner);
|
||||||
inner
|
inner
|
||||||
.添加(Arc::clone(&待添加虚线.borrow().inner))
|
.添加(Arc::clone(&待添加虚线.borrow().inner))
|
||||||
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e))
|
.map_err(pyo3::exceptions::PyValueError::new_err)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// :param 待删除虚线: 待删除的虚线
|
/// :param 待删除虚线: 待删除的虚线
|
||||||
@@ -1156,7 +1166,7 @@ impl 线段特征Py {
|
|||||||
let inner = Arc::make_mut(&mut self.inner);
|
let inner = Arc::make_mut(&mut self.inner);
|
||||||
inner
|
inner
|
||||||
.删除(&Arc::clone(&待删除虚线.borrow().inner))
|
.删除(&Arc::clone(&待删除虚线.borrow().inner))
|
||||||
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e))
|
.map_err(pyo3::exceptions::PyValueError::new_err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- classmethods ----
|
// ---- classmethods ----
|
||||||
|
|||||||
@@ -545,7 +545,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|||||||
// 买卖点类型 class attributes (singleton instances)
|
// 买卖点类型 class attributes (singleton instances)
|
||||||
let py = m.py();
|
let py = m.py();
|
||||||
let bsp_class = m.getattr("买卖点类型")?;
|
let bsp_class = m.getattr("买卖点类型")?;
|
||||||
let bsp_class = bsp_class.downcast_into::<PyType>()?;
|
let bsp_class = bsp_class.cast_into::<PyType>()?;
|
||||||
|
|
||||||
let variants: &[(&str, chanlun::types::买卖点类型)] = &[
|
let variants: &[(&str, chanlun::types::买卖点类型)] = &[
|
||||||
("一买", chanlun::types::买卖点类型::一买),
|
("一买", chanlun::types::买卖点类型::一买),
|
||||||
@@ -568,7 +568,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|||||||
("T3B卖", chanlun::types::买卖点类型::T3B卖),
|
("T3B卖", chanlun::types::买卖点类型::T3B卖),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut bsp_members = PyDict::new(py);
|
let bsp_members = PyDict::new(py);
|
||||||
for (name, value) in variants {
|
for (name, value) in variants {
|
||||||
let instance = Py::new(py, 买卖点类型Py { inner: *value })?;
|
let instance = Py::new(py, 买卖点类型Py { inner: *value })?;
|
||||||
bsp_class.setattr(*name, instance.clone_ref(py))?;
|
bsp_class.setattr(*name, instance.clone_ref(py))?;
|
||||||
@@ -577,7 +577,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|||||||
bsp_class.setattr("__members__", bsp_members)?;
|
bsp_class.setattr("__members__", bsp_members)?;
|
||||||
|
|
||||||
// 相对方向 class attributes
|
// 相对方向 class attributes
|
||||||
let dir_class = m.getattr("相对方向")?.downcast_into::<PyType>()?.clone();
|
let dir_class = m.getattr("相对方向")?.cast_into::<PyType>()?.clone();
|
||||||
let dir_variants: &[(&str, chanlun::types::相对方向)] = &[
|
let dir_variants: &[(&str, chanlun::types::相对方向)] = &[
|
||||||
("向上", chanlun::types::相对方向::向上),
|
("向上", chanlun::types::相对方向::向上),
|
||||||
("向下", chanlun::types::相对方向::向下),
|
("向下", chanlun::types::相对方向::向下),
|
||||||
@@ -590,7 +590,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|||||||
("同", chanlun::types::相对方向::同),
|
("同", chanlun::types::相对方向::同),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut dir_members = PyDict::new(py);
|
let dir_members = PyDict::new(py);
|
||||||
for (name, value) in dir_variants {
|
for (name, value) in dir_variants {
|
||||||
let instance = Py::new(py, 相对方向Py { inner: *value })?;
|
let instance = Py::new(py, 相对方向Py { inner: *value })?;
|
||||||
dir_class.setattr(*name, instance.clone_ref(py))?;
|
dir_class.setattr(*name, instance.clone_ref(py))?;
|
||||||
@@ -599,7 +599,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|||||||
dir_class.setattr("__members__", dir_members)?;
|
dir_class.setattr("__members__", dir_members)?;
|
||||||
|
|
||||||
// 分型结构 class attributes
|
// 分型结构 class attributes
|
||||||
let frac_class = m.getattr("分型结构")?.downcast_into::<PyType>()?.clone();
|
let frac_class = m.getattr("分型结构")?.cast_into::<PyType>()?.clone();
|
||||||
let frac_variants: &[(&str, chanlun::types::分型结构)] = &[
|
let frac_variants: &[(&str, chanlun::types::分型结构)] = &[
|
||||||
("上", chanlun::types::分型结构::上),
|
("上", chanlun::types::分型结构::上),
|
||||||
("下", chanlun::types::分型结构::下),
|
("下", chanlun::types::分型结构::下),
|
||||||
@@ -608,7 +608,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|||||||
("散", chanlun::types::分型结构::散),
|
("散", chanlun::types::分型结构::散),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut frac_members = PyDict::new(py);
|
let frac_members = PyDict::new(py);
|
||||||
for (name, value) in frac_variants {
|
for (name, value) in frac_variants {
|
||||||
let instance = Py::new(py, 分型结构Py { inner: *value })?;
|
let instance = Py::new(py, 分型结构Py { inner: *value })?;
|
||||||
frac_class.setattr(*name, instance.clone_ref(py))?;
|
frac_class.setattr(*name, instance.clone_ref(py))?;
|
||||||
|
|||||||
@@ -566,6 +566,30 @@ impl 中枢 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<_>>()
|
||||||
|
.join(", ");
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}({}, {}, 元素数量: {}, [{}], {} ===>>> {})",
|
||||||
|
self.标识.read().unwrap(),
|
||||||
|
crate::utils::format_f64_g(self.高()),
|
||||||
|
crate::utils::format_f64_g(self.低()),
|
||||||
|
self.基础序列.read().unwrap().len(),
|
||||||
|
序列_str,
|
||||||
|
self.文(),
|
||||||
|
self.武(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -575,13 +599,14 @@ mod tests {
|
|||||||
use crate::types::分型结构;
|
use crate::types::分型结构;
|
||||||
|
|
||||||
fn 辅助_创建K线(时间戳: i64, 高: f64, 低: f64, 开: f64, 收: f64) -> K线 {
|
fn 辅助_创建K线(时间戳: i64, 高: f64, 低: f64, 开: f64, 收: f64) -> K线 {
|
||||||
let mut k = K线::default();
|
K线 {
|
||||||
k.时间戳 = 时间戳;
|
时间戳,
|
||||||
k.高 = 高;
|
高,
|
||||||
k.低 = 低;
|
低,
|
||||||
k.开盘价 = 开;
|
开盘价: 开,
|
||||||
k.收盘价 = 收;
|
收盘价: 收,
|
||||||
k
|
..Default::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn 辅助_创建缠K(
|
fn 辅助_创建缠K(
|
||||||
@@ -710,7 +735,7 @@ mod tests {
|
|||||||
中枢.设置第三买卖线(Arc::clone(&笔1));
|
中枢.设置第三买卖线(Arc::clone(&笔1));
|
||||||
assert!(中枢.第三买卖线.read().unwrap().is_some());
|
assert!(中枢.第三买卖线.read().unwrap().is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Arc::as_ptr(&*中枢.第三买卖线.read().unwrap().as_ref().unwrap()),
|
Arc::as_ptr(中枢.第三买卖线.read().unwrap().as_ref().unwrap()),
|
||||||
Arc::as_ptr(&笔1)
|
Arc::as_ptr(&笔1)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -870,27 +895,3 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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<_>>()
|
|
||||||
.join(", ");
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}({}, {}, 元素数量: {}, [{}], {} ===>>> {})",
|
|
||||||
self.标识.read().unwrap(),
|
|
||||||
crate::utils::format_f64_g(self.高()),
|
|
||||||
crate::utils::format_f64_g(self.低()),
|
|
||||||
self.基础序列.read().unwrap().len(),
|
|
||||||
序列_str,
|
|
||||||
self.文(),
|
|
||||||
self.武(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -590,7 +590,7 @@ mod tests {
|
|||||||
let config = 缠论配置::default();
|
let config = 缠论配置::default();
|
||||||
let obs_ref = 观察者::new("btcusd".into(), 300, config);
|
let obs_ref = 观察者::new("btcusd".into(), 300, config);
|
||||||
|
|
||||||
let data = std::fs::read(&test_data_path()).unwrap();
|
let data = std::fs::read(test_data_path()).unwrap();
|
||||||
let size = 48;
|
let size = 48;
|
||||||
|
|
||||||
for i in 0..data.len() / size {
|
for i in 0..data.len() / size {
|
||||||
@@ -720,7 +720,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_重复计算后结果一致() {
|
fn test_重复计算后结果一致() {
|
||||||
let data = std::fs::read(&test_data_path()).unwrap();
|
let data = std::fs::read(test_data_path()).unwrap();
|
||||||
let size = 48;
|
let size = 48;
|
||||||
|
|
||||||
let 计算 = || {
|
let 计算 = || {
|
||||||
@@ -764,7 +764,7 @@ mod tests {
|
|||||||
let config = 缠论配置::default();
|
let config = 缠论配置::default();
|
||||||
let obs_ref = 观察者::new("btcusd".into(), 300, config);
|
let obs_ref = 观察者::new("btcusd".into(), 300, config);
|
||||||
|
|
||||||
let data = std::fs::read(&test_data_path()).unwrap();
|
let data = std::fs::read(test_data_path()).unwrap();
|
||||||
let size = 48;
|
let size = 48;
|
||||||
|
|
||||||
for i in 0..data.len() / size {
|
for i in 0..data.len() / size {
|
||||||
|
|||||||
@@ -960,6 +960,49 @@ impl 虚线 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for 虚线 {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if *self.标识.read().unwrap() == "笔" {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"笔({}, {}, {}, {}, 周期: {}, 数量: {})",
|
||||||
|
self.序号.load(Ordering::Relaxed),
|
||||||
|
self.方向(),
|
||||||
|
self.文,
|
||||||
|
self.武.read().unwrap(),
|
||||||
|
self.文.中.周期,
|
||||||
|
self.武.read().unwrap().中.序号.load(Ordering::Relaxed)
|
||||||
|
- self.文.中.序号.load(Ordering::Relaxed)
|
||||||
|
+ 1
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let 四象 = crate::algorithm::segment::线段::四象(self);
|
||||||
|
let 缺口 = crate::algorithm::segment::线段::获取缺口(self);
|
||||||
|
let 缺口_str = match 缺口 {
|
||||||
|
Some(g) => format!("{}", g),
|
||||||
|
None => "None".to_string(),
|
||||||
|
};
|
||||||
|
let 确认K线_str = match &*self.确认K线.read().unwrap() {
|
||||||
|
Some(k) => format!("{}", k),
|
||||||
|
None => "None".to_string(),
|
||||||
|
};
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}<{}, {}, {}, {}, {}, 数量: {}, 缺口: {}, {}>",
|
||||||
|
self.标识.read().unwrap(),
|
||||||
|
self.序号.load(Ordering::Relaxed),
|
||||||
|
四象,
|
||||||
|
self.方向(),
|
||||||
|
self.文,
|
||||||
|
self.武.read().unwrap(),
|
||||||
|
self.基础序列.read().unwrap().len(),
|
||||||
|
缺口_str,
|
||||||
|
确认K线_str,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -969,13 +1012,14 @@ mod tests {
|
|||||||
|
|
||||||
/// 辅助:创建一根最小化的原始K线
|
/// 辅助:创建一根最小化的原始K线
|
||||||
fn 辅助_创建K线(时间戳: i64, 高: f64, 低: f64, 开: f64, 收: f64) -> K线 {
|
fn 辅助_创建K线(时间戳: i64, 高: f64, 低: f64, 开: f64, 收: f64) -> K线 {
|
||||||
let mut k = K线::default();
|
K线 {
|
||||||
k.时间戳 = 时间戳;
|
时间戳,
|
||||||
k.高 = 高;
|
高,
|
||||||
k.低 = 低;
|
低,
|
||||||
k.开盘价 = 开;
|
开盘价: 开,
|
||||||
k.收盘价 = 收;
|
收盘价: 收,
|
||||||
k
|
..Default::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 辅助:创建一根缠论K线
|
/// 辅助:创建一根缠论K线
|
||||||
@@ -1239,46 +1283,3 @@ mod tests {
|
|||||||
assert_eq!(新武耗时, 300);
|
assert_eq!(新武耗时, 300);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for 虚线 {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
if *self.标识.read().unwrap() == "笔" {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"笔({}, {}, {}, {}, 周期: {}, 数量: {})",
|
|
||||||
self.序号.load(Ordering::Relaxed),
|
|
||||||
self.方向(),
|
|
||||||
self.文,
|
|
||||||
self.武.read().unwrap(),
|
|
||||||
self.文.中.周期,
|
|
||||||
self.武.read().unwrap().中.序号.load(Ordering::Relaxed)
|
|
||||||
- self.文.中.序号.load(Ordering::Relaxed)
|
|
||||||
+ 1
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let 四象 = crate::algorithm::segment::线段::四象(self);
|
|
||||||
let 缺口 = crate::algorithm::segment::线段::获取缺口(self);
|
|
||||||
let 缺口_str = match 缺口 {
|
|
||||||
Some(g) => format!("{}", g),
|
|
||||||
None => "None".to_string(),
|
|
||||||
};
|
|
||||||
let 确认K线_str = match &*self.确认K线.read().unwrap() {
|
|
||||||
Some(k) => format!("{}", k),
|
|
||||||
None => "None".to_string(),
|
|
||||||
};
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}<{}, {}, {}, {}, {}, 数量: {}, 缺口: {}, {}>",
|
|
||||||
self.标识.read().unwrap(),
|
|
||||||
self.序号.load(Ordering::Relaxed),
|
|
||||||
四象,
|
|
||||||
self.方向(),
|
|
||||||
self.文,
|
|
||||||
self.武.read().unwrap(),
|
|
||||||
self.基础序列.read().unwrap().len(),
|
|
||||||
缺口_str,
|
|
||||||
确认K线_str,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -320,13 +320,14 @@ mod tests {
|
|||||||
use crate::types::分型结构;
|
use crate::types::分型结构;
|
||||||
|
|
||||||
fn 辅助_创建K线(时间戳: i64, 高: f64, 低: f64, 开: f64, 收: f64) -> K线 {
|
fn 辅助_创建K线(时间戳: i64, 高: f64, 低: f64, 开: f64, 收: f64) -> K线 {
|
||||||
let mut k = K线::default();
|
K线 {
|
||||||
k.时间戳 = 时间戳;
|
时间戳,
|
||||||
k.高 = 高;
|
高,
|
||||||
k.低 = 低;
|
低,
|
||||||
k.开盘价 = 开;
|
开盘价: 开,
|
||||||
k.收盘价 = 收;
|
收盘价: 收,
|
||||||
k
|
..Default::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn 辅助_创建缠K(
|
fn 辅助_创建缠K(
|
||||||
|
|||||||
+5
-2
@@ -770,7 +770,7 @@ class 高级策略基类(bt.Strategy):
|
|||||||
|
|
||||||
def 日志(self, 文本, 时间=None):
|
def 日志(self, 文本, 时间=None):
|
||||||
时间 = 时间 or self.datas[0].datetime.datetime(0)
|
时间 = 时间 or self.datas[0].datetime.datetime(0)
|
||||||
print(f"{时间} {文本}")
|
print(f"{时间} {self.p.观察员.__class__.__name__}: {文本}")
|
||||||
|
|
||||||
def 计算目标数量(self, 价格):
|
def 计算目标数量(self, 价格):
|
||||||
"""根据资金类型和仓位比例计算目标数量"""
|
"""根据资金类型和仓位比例计算目标数量"""
|
||||||
@@ -971,6 +971,7 @@ class 回测(高级策略基类):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def next(self):
|
def next(self):
|
||||||
|
# self.日志(f"{self.p.观察员.__class__.__name__} called next ")
|
||||||
# 1. 更新移动止损(基类方法)
|
# 1. 更新移动止损(基类方法)
|
||||||
if self.position:
|
if self.position:
|
||||||
self.更新止损订单(self.position.size > 0, self.data.close[0])
|
self.更新止损订单(self.position.size > 0, self.data.close[0])
|
||||||
@@ -1000,6 +1001,7 @@ class 回测(高级策略基类):
|
|||||||
def 检查买信号(self):
|
def 检查买信号(self):
|
||||||
if self.p.观察员.笔序列:
|
if self.p.观察员.笔序列:
|
||||||
k线 = self.p.观察员.缠论K线序列[-1]
|
k线 = self.p.观察员.缠论K线序列[-1]
|
||||||
|
self.日志(f"检查买信号 当前笔 {self.p.观察员.笔序列[-1]}")
|
||||||
if k线.买卖点信息:
|
if k线.买卖点信息:
|
||||||
print(f"回测-首 {self.p.观察员.__class__.__name__}", k线.买卖点信息)
|
print(f"回测-首 {self.p.观察员.__class__.__name__}", k线.买卖点信息)
|
||||||
首 = True if k线.买卖点信息 and "买" in next(iter(k线.买卖点信息)) else False
|
首 = True if k线.买卖点信息 and "买" in next(iter(k线.买卖点信息)) else False
|
||||||
@@ -1022,6 +1024,7 @@ class 回测(高级策略基类):
|
|||||||
|
|
||||||
def 检查卖信号(self):
|
def 检查卖信号(self):
|
||||||
if self.p.观察员.笔序列:
|
if self.p.观察员.笔序列:
|
||||||
|
self.日志(f"检查卖信号 当前笔 {self.p.观察员.笔序列[-1]}")
|
||||||
k线 = self.p.观察员.缠论K线序列[-1]
|
k线 = self.p.观察员.缠论K线序列[-1]
|
||||||
if k线.买卖点信息:
|
if k线.买卖点信息:
|
||||||
print(f"回测-首 {self.p.观察员.__class__.__name__}", k线.买卖点信息)
|
print(f"回测-首 {self.p.观察员.__class__.__name__}", k线.买卖点信息)
|
||||||
@@ -1045,7 +1048,7 @@ class 回测(高级策略基类):
|
|||||||
|
|
||||||
def log(self, 文本, dt=None):
|
def log(self, 文本, dt=None):
|
||||||
dt = dt or bt.num2date(self.data.datetime[0])
|
dt = dt or bt.num2date(self.data.datetime[0])
|
||||||
print(f"[{dt.strftime('%Y-%m-%d %H:%M')}] {self.p.符号} | {文本}")
|
print(f"[{dt.strftime('%Y-%m-%d %H:%M')}] {self.p.观察员.__class__.__name__}: {self.p.符号} | {文本}")
|
||||||
|
|
||||||
|
|
||||||
# ==================== 回测运行入口 ====================
|
# ==================== 回测运行入口 ====================
|
||||||
|
|||||||
Reference in New Issue
Block a user