添加 原始chan到模块
修复 相对方向的一致性 添加 观察者.投喂原始数据
This commit is contained in:
@@ -1214,10 +1214,8 @@ impl 中枢Py {
|
||||
|
||||
#[getter]
|
||||
/// :return: 中枢方向(首条虚线的方向翻转)
|
||||
fn 方向(&self) -> 相对方向Py {
|
||||
相对方向Py {
|
||||
inner: self.inner.方向(),
|
||||
}
|
||||
fn 方向(&self, py: Python<'_>) -> Py<相对方向Py> {
|
||||
crate::types_py::获取相对方向单例(py, self.inner.方向())
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -1328,7 +1326,7 @@ impl 中枢Py {
|
||||
dict.set_item("标识", self.标识())?;
|
||||
dict.set_item("级别", self.级别())?;
|
||||
dict.set_item("图表标题", self.图表标题())?;
|
||||
dict.set_item("方向", self.方向())?;
|
||||
dict.set_item("方向", self.方向(py))?;
|
||||
dict.set_item("高", self.高())?;
|
||||
dict.set_item("低", self.低())?;
|
||||
dict.set_item("高高", self.高高())?;
|
||||
|
||||
@@ -560,30 +560,30 @@ impl 观察者Py {
|
||||
self.obs_mut().增加原始K线((*普K.borrow().inner).clone());
|
||||
}
|
||||
|
||||
/// 投喂原始数据 — 便捷入口,直接从 OHLCV 创建 K线 并投喂
|
||||
fn 投喂原始数据(
|
||||
&mut self, 时间戳: i64, 开: f64, 高: f64, 低: f64, 收: f64, 量: f64
|
||||
) {
|
||||
self.obs_mut().投喂原始数据(时间戳, 开, 高, 低, 收, 量);
|
||||
}
|
||||
|
||||
/// 加载本地数据 — 从 .nb 文件加载K线数据(先重置,再通过 Python dispatch 逐根投喂,
|
||||
/// 确保子类重写的 增加原始K线 被正确调用)。
|
||||
fn 加载本地数据(slf: &Bound<'_, Self>, 文件路径: &str) -> PyResult<()> {
|
||||
let py = slf.py();
|
||||
|
||||
// 重置基础序列
|
||||
slf.borrow_mut().obs_mut().重置基础序列();
|
||||
|
||||
// 解析文件得到 K线 列表
|
||||
let bars = slf
|
||||
.borrow()
|
||||
.obs()
|
||||
.解析本地数据(文件路径)
|
||||
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e))?;
|
||||
|
||||
// 通过 Python dispatch 逐根投喂,确保子类重写生效
|
||||
for k线 in bars {
|
||||
let k线_py = Py::new(
|
||||
py,
|
||||
K线Py {
|
||||
inner: Arc::new(k线),
|
||||
},
|
||||
)?;
|
||||
slf.call_method1("增加原始K线", (k线_py,))?;
|
||||
// 读取文件,通过 Python dispatch 逐根投喂(支持子类重写 增加原始K线)
|
||||
let data = std::fs::read(文件路径)
|
||||
.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("read file: {}", e)))?;
|
||||
let size: usize = 48;
|
||||
for i in 0..data.len() / size {
|
||||
let offset = i * size;
|
||||
if let Some((时间戳, 开, 高, 低, 收, 量)) =
|
||||
chanlun::kline::bar::K线::解析原始数据(&data[offset..offset + size])
|
||||
{
|
||||
slf.call_method1("投喂原始数据", (时间戳, 开, 高, 低, 收, 量))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -600,10 +600,14 @@ impl 观察者Py {
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
#[pyo3(signature = (文件路径, 配置 = None))]
|
||||
#[pyo3(signature = (观察员, 文件路径, 配置 = None))]
|
||||
/// :param 观察员: 观察者实例
|
||||
/// :param 文件路径: 数据文件路径 格式如: btcusd-300-1631772074-1632222374.nb
|
||||
/// :param 配置: 缠论配置
|
||||
/// :return: 观察者实例
|
||||
fn 读取数据文件(
|
||||
cls: &Bound<'_, PyType>,
|
||||
_cls: &Bound<'_, PyType>,
|
||||
观察员: &Bound<'_, Self>,
|
||||
文件路径: &str,
|
||||
配置: Option<&Bound<'_, 缠论配置Py>>,
|
||||
py: Python<'_>,
|
||||
@@ -631,31 +635,19 @@ impl 观察者Py {
|
||||
.parse()
|
||||
.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("parse period: {}", e)))?;
|
||||
|
||||
// 通过 cls 构造实例(支持子类化)
|
||||
let cfg_py = 缠论配置Py::from_rust_config(&config)?;
|
||||
let cfg_obj = Py::new(py, cfg_py)?;
|
||||
let obj = cls.call1((符号.clone(), 周期, cfg_obj))?;
|
||||
|
||||
// 读取文件并通过 Python 分发逐根投喂(支持子类重写 增加原始K线)
|
||||
let data = std::fs::read(文件路径)
|
||||
.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("read file: {}", e)))?;
|
||||
let size: usize = 48;
|
||||
for i in 0..data.len() / size {
|
||||
let offset = i * size;
|
||||
if let Some(k线) =
|
||||
chanlun::kline::bar::K线::from_bytes(&data[offset..offset + size], 周期, &符号)
|
||||
{
|
||||
let k线_py = Py::new(
|
||||
py,
|
||||
K线Py {
|
||||
inner: Arc::new(k线),
|
||||
},
|
||||
)?;
|
||||
obj.call_method1("增加原始K线", (k线_py,))?;
|
||||
}
|
||||
// 设置观察员属性
|
||||
{
|
||||
let slf_ref = 观察员.borrow_mut();
|
||||
let mut obs_mut = slf_ref.obs_mut();
|
||||
obs_mut.符号 = 符号;
|
||||
obs_mut.周期 = 周期;
|
||||
obs_mut.配置 = config;
|
||||
}
|
||||
|
||||
Ok(obj.unbind())
|
||||
// 调用加载本地数据
|
||||
观察员.call_method1("加载本地数据", (文件路径,))?;
|
||||
|
||||
Ok(观察员.clone().unbind().into())
|
||||
}
|
||||
|
||||
// ---- 序列 getters ----
|
||||
|
||||
@@ -139,10 +139,8 @@ impl K线Py {
|
||||
|
||||
#[getter]
|
||||
/// :return: 相对方向.向上(开盘<收盘)或 相对方向.向下(开盘>收盘)
|
||||
fn 方向(&self) -> 相对方向Py {
|
||||
相对方向Py {
|
||||
inner: self.inner.方向(),
|
||||
}
|
||||
fn 方向(&self, py: Python<'_>) -> Py<相对方向Py> {
|
||||
crate::types_py::获取相对方向单例(py, self.inner.方向())
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -182,7 +180,7 @@ impl K线Py {
|
||||
dict.set_item("开盘价", self.开盘价())?;
|
||||
dict.set_item("收盘价", self.收盘价())?;
|
||||
dict.set_item("成交量", self.成交量())?;
|
||||
dict.set_item("方向", self.方向())?;
|
||||
dict.set_item("方向", self.方向(py))?;
|
||||
if let Some(v) = self.macd() {
|
||||
dict.set_item("macd", v)?;
|
||||
}
|
||||
@@ -443,10 +441,8 @@ impl 缠论K线Py {
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 方向(&self) -> 相对方向Py {
|
||||
相对方向Py {
|
||||
inner: *self.inner.方向.read().unwrap(),
|
||||
}
|
||||
fn 方向(&self, py: Python<'_>) -> Py<相对方向Py> {
|
||||
crate::types_py::获取相对方向单例(py, *self.inner.方向.read().unwrap())
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -496,7 +492,7 @@ impl 缠论K线Py {
|
||||
dict.set_item("时间戳", self.时间戳())?;
|
||||
dict.set_item("高", self.高())?;
|
||||
dict.set_item("低", self.低())?;
|
||||
dict.set_item("方向", self.方向())?;
|
||||
dict.set_item("方向", self.方向(py))?;
|
||||
dict.set_item("周期", self.周期())?;
|
||||
dict.set_item("标识", self.标识())?;
|
||||
dict.set_item("分型特征值", self.分型特征值())?;
|
||||
|
||||
+30
-29
@@ -71,39 +71,40 @@ fn _chanlun(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::*;
|
||||
use pyo3::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_rc_pointer_across_getters() {
|
||||
pyo3::prepare_freethreaded_python();
|
||||
Python::with_gil(|py| {
|
||||
fn test_分型模式_get_set() {
|
||||
// 手动初始化 Python 解释器(cargo test 环境下 auto-initialize 不一定生效)
|
||||
unsafe {
|
||||
if pyo3::ffi::Py_IsInitialized() == 0 {
|
||||
pyo3::ffi::Py_Initialize();
|
||||
}
|
||||
}
|
||||
pyo3::Python::try_attach(|py| {
|
||||
let module = PyModule::new(py, "test_module").unwrap();
|
||||
module.add_class::<business_py::观察者Py>().unwrap();
|
||||
module.add_class::<business_py::基础买卖点Py>().unwrap();
|
||||
module.add_class::<business_py::买卖点Py>().unwrap();
|
||||
module.add_class::<kline_py::K线Py>().unwrap();
|
||||
module.add_class::<kline_py::缠论K线Py>().unwrap();
|
||||
module.add_class::<structure_py::分型Py>().unwrap();
|
||||
module.add_class::<structure_py::虚线Py>().unwrap();
|
||||
module.add_class::<config_py::缠论配置Py>().unwrap();
|
||||
module
|
||||
.add_function(wrap_pyfunction!(get_分型模式, &module).unwrap())
|
||||
.unwrap();
|
||||
module
|
||||
.add_function(wrap_pyfunction!(set_分型模式, &module).unwrap())
|
||||
.unwrap();
|
||||
|
||||
let config = config_py::缠论配置Py::from_rust_config(&Default::default()).unwrap();
|
||||
let obs = business_py::观察者Py::new_impl("btcusd".into(), 300, config, py).unwrap();
|
||||
// 默认 true
|
||||
let getter = module.getattr("get_分型模式").unwrap();
|
||||
let result: bool = getter.call0().unwrap().extract().unwrap();
|
||||
assert!(result, "分型模式 默认应为 True");
|
||||
|
||||
// Feed one K line
|
||||
let kline = kline_py::K线Py::new_impl(
|
||||
"btcusd".into(),
|
||||
1000,
|
||||
100.0,
|
||||
105.0,
|
||||
99.0,
|
||||
103.0,
|
||||
1000.0,
|
||||
0,
|
||||
300,
|
||||
);
|
||||
let kline_ref = kline.into_ref(py);
|
||||
// ... this is too complex
|
||||
});
|
||||
// 设置为 false
|
||||
let setter = module.getattr("set_分型模式").unwrap();
|
||||
setter.call1((false,)).unwrap();
|
||||
let result: bool = getter.call0().unwrap().extract().unwrap();
|
||||
assert!(!result, "分型模式 应为 False");
|
||||
|
||||
// 恢复 true
|
||||
setter.call1((true,)).unwrap();
|
||||
let result: bool = getter.call0().unwrap().extract().unwrap();
|
||||
assert!(result, "分型模式 应为 True");
|
||||
})
|
||||
.expect("Python 解释器初始化后 attach 仍失败");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,12 +233,15 @@ impl 分型Py {
|
||||
|
||||
#[getter]
|
||||
/// 左、中、右三对相对方向关系
|
||||
fn 关系组(&self) -> Option<(相对方向Py, 相对方向Py, 相对方向Py)> {
|
||||
fn 关系组(
|
||||
&self,
|
||||
py: Python<'_>,
|
||||
) -> Option<(Py<相对方向Py>, Py<相对方向Py>, Py<相对方向Py>)> {
|
||||
self.inner.关系组().map(|(a, b, c)| {
|
||||
(
|
||||
相对方向Py { inner: a },
|
||||
相对方向Py { inner: b },
|
||||
相对方向Py { inner: c },
|
||||
crate::types_py::获取相对方向单例(py, a),
|
||||
crate::types_py::获取相对方向单例(py, b),
|
||||
crate::types_py::获取相对方向单例(py, c),
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -271,7 +274,7 @@ impl 分型Py {
|
||||
if let Some(v) = self.右(py) {
|
||||
dict.set_item("右", v)?;
|
||||
}
|
||||
if let Some(v) = self.关系组() {
|
||||
if let Some(v) = self.关系组(py) {
|
||||
dict.set_item("关系组", v)?;
|
||||
}
|
||||
Ok(dict.into())
|
||||
@@ -545,10 +548,8 @@ impl 虚线Py {
|
||||
|
||||
#[getter]
|
||||
/// :return: 运行方向
|
||||
fn 方向(&self) -> 相对方向Py {
|
||||
相对方向Py {
|
||||
inner: self.inner.方向(),
|
||||
}
|
||||
fn 方向(&self, py: Python<'_>) -> Py<相对方向Py> {
|
||||
crate::types_py::获取相对方向单例(py, self.inner.方向())
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -1036,10 +1037,8 @@ impl 线段特征Py {
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 线段方向(&self) -> 相对方向Py {
|
||||
相对方向Py {
|
||||
inner: self.inner.线段方向,
|
||||
}
|
||||
fn 线段方向(&self, py: Python<'_>) -> Py<相对方向Py> {
|
||||
crate::types_py::获取相对方向单例(py, self.inner.线段方向)
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -1101,7 +1100,7 @@ impl 线段特征Py {
|
||||
let dict = PyDict::new(py);
|
||||
dict.set_item("序号", self.序号())?;
|
||||
dict.set_item("标识", self.标识())?;
|
||||
dict.set_item("线段方向", self.线段方向())?;
|
||||
dict.set_item("线段方向", self.线段方向(py))?;
|
||||
dict.set_item("图表标题", self.图表标题())?;
|
||||
Ok(dict.into())
|
||||
}
|
||||
@@ -1128,10 +1127,8 @@ impl 线段特征Py {
|
||||
|
||||
#[getter]
|
||||
/// :return: 特征序列方向(线段方向的翻转)
|
||||
fn 方向(&self) -> 相对方向Py {
|
||||
相对方向Py {
|
||||
inner: self.inner.方向(),
|
||||
}
|
||||
fn 方向(&self, py: Python<'_>) -> Py<相对方向Py> {
|
||||
crate::types_py::获取相对方向单例(py, self.inner.方向())
|
||||
}
|
||||
|
||||
#[getter]
|
||||
|
||||
@@ -61,6 +61,40 @@ pub fn 获取分型结构单例(
|
||||
result
|
||||
}
|
||||
|
||||
static 相对方向_单例缓存: Mutex<Option<HashMap<u8, Py<相对方向Py>>>> = Mutex::new(None);
|
||||
|
||||
pub fn 获取相对方向单例(
|
||||
py: Python<'_>,
|
||||
inner: chanlun::types::相对方向,
|
||||
) -> Py<相对方向Py> {
|
||||
let mut guard = 相对方向_单例缓存.lock().unwrap();
|
||||
if let Some(ref map) = *guard {
|
||||
return map[&(inner as u8)].clone_ref(py);
|
||||
}
|
||||
|
||||
// 首次访问时从类属性加载单例
|
||||
let module = py.import("chanlun._chanlun").unwrap();
|
||||
let class = module.getattr("相对方向").unwrap();
|
||||
let mut map = HashMap::new();
|
||||
for (name, variant) in &[
|
||||
("向上", chanlun::types::相对方向::向上),
|
||||
("向下", chanlun::types::相对方向::向下),
|
||||
("向上缺口", chanlun::types::相对方向::向上缺口),
|
||||
("向下缺口", chanlun::types::相对方向::向下缺口),
|
||||
("衔接向上", chanlun::types::相对方向::衔接向上),
|
||||
("衔接向下", chanlun::types::相对方向::衔接向下),
|
||||
("顺", chanlun::types::相对方向::顺),
|
||||
("逆", chanlun::types::相对方向::逆),
|
||||
("同", chanlun::types::相对方向::同),
|
||||
] {
|
||||
let instance: Py<相对方向Py> = class.getattr(*name).unwrap().extract().unwrap();
|
||||
map.insert(*variant as u8, instance);
|
||||
}
|
||||
let result = map[&(inner as u8)].clone_ref(py);
|
||||
*guard = Some(map);
|
||||
result
|
||||
}
|
||||
|
||||
// ========== 买卖点类型 ==========
|
||||
|
||||
/// 买卖点类型 — 缠论的三类买卖点及扩展类型。
|
||||
@@ -189,10 +223,8 @@ impl 相对方向Py {
|
||||
}
|
||||
|
||||
/// 返回方向的对立面(向上↔向下, 缺口↔反向缺口, 衔接↔反向衔接)。
|
||||
fn 翻转(&self) -> Self {
|
||||
Self {
|
||||
inner: self.inner.翻转(),
|
||||
}
|
||||
fn 翻转(&self, py: Python<'_>) -> Py<Self> {
|
||||
获取相对方向单例(py, self.inner.翻转())
|
||||
}
|
||||
|
||||
/// 判断是否为向上方向(向上/向上缺口/衔接向上)
|
||||
@@ -245,10 +277,11 @@ impl 相对方向Py {
|
||||
#[classmethod]
|
||||
fn 分析(
|
||||
_cls: &Bound<'_, PyType>, 前高: f64, 前低: f64, 后高: f64, 后低: f64
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: chanlun::types::相对方向::分析(前高, 前低, 后高, 后低),
|
||||
}
|
||||
) -> Py<Self> {
|
||||
获取相对方向单例(
|
||||
_cls.py(),
|
||||
chanlun::types::相对方向::分析(前高, 前低, 后高, 后低),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user