1432 lines
47 KiB
Rust
1432 lines
47 KiB
Rust
/*
|
||
* MIT License
|
||
*
|
||
* Copyright (c) 2026 YuYuKunKun
|
||
*
|
||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
* of this software and associated documentation files (the "Software"), to deal
|
||
* in the Software without restriction, including without limitation the rights
|
||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
* copies of the Software, and to permit persons to whom the Software is
|
||
* furnished to do so, subject to the following conditions:
|
||
*
|
||
* The above copyright notice and this permission notice shall be included in all
|
||
* copies or substantial portions of the Software.
|
||
*
|
||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
* SOFTWARE.
|
||
*/
|
||
|
||
use pyo3::prelude::*;
|
||
use pyo3::types::{PyDict, PyType};
|
||
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, 分型Py};
|
||
use std::collections::HashMap;
|
||
use std::sync::Arc;
|
||
|
||
use crate::config_py::缠论配置Py;
|
||
use crate::kline_py::{K线Py, 缠论K线Py};
|
||
use crate::types_py::买卖点类型Py;
|
||
|
||
// ========== 基础买卖点 ==========
|
||
|
||
/// 基础买卖点 — 买卖点的数据结构,描述偏离买入/卖出位置的程度。
|
||
///
|
||
/// 属性:
|
||
/// 偏移: int — 买卖点距当前K线的偏差
|
||
/// 失效偏移: int — 买卖点失效的最大偏差
|
||
/// 有效性: bool — 买卖点是否仍有效
|
||
/// 与MACD柱子匹配: bool|None — 是否与MACD柱状图方向匹配
|
||
/// 与MACD柱子分型匹配: bool|None — 是否与MACD柱分型匹配
|
||
#[pyclass(
|
||
name = "基础买卖点",
|
||
module = "chanlun._chanlun",
|
||
subclass,
|
||
from_py_object
|
||
)]
|
||
#[derive(Clone)]
|
||
pub struct 基础买卖点Py {
|
||
pub(crate) inner: chanlun::business::bsp::基础买卖点,
|
||
}
|
||
|
||
#[pymethods]
|
||
impl 基础买卖点Py {
|
||
#[new]
|
||
fn new(
|
||
类型: &Bound<'_, 买卖点类型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
) -> Self {
|
||
Self {
|
||
inner: chanlun::business::bsp::基础买卖点::new(
|
||
类型.borrow().inner,
|
||
当前K线.borrow().inner.clone(),
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
}
|
||
}
|
||
|
||
#[getter]
|
||
fn 备注(&self) -> String {
|
||
self.inner.备注.clone()
|
||
}
|
||
|
||
#[setter]
|
||
#[pyo3(name = "备注")]
|
||
fn 设置_备注(&mut self, value: String) {
|
||
self.inner.备注 = value;
|
||
}
|
||
|
||
#[getter]
|
||
fn 类型(&self) -> 买卖点类型Py {
|
||
买卖点类型Py {
|
||
inner: self.inner.类型,
|
||
}
|
||
}
|
||
|
||
#[getter]
|
||
fn 买卖点分型(&self, py: Python<'_>) -> Py<分型Py> {
|
||
fractal_to_py(py, Arc::clone(&self.inner.买卖点分型))
|
||
}
|
||
|
||
#[getter]
|
||
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, py: Python<'_>) -> Py<K线Py> {
|
||
bar_to_py(py, self.inner.当前K线.clone())
|
||
}
|
||
|
||
#[getter]
|
||
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, py: Python<'_>) -> Option<Py<K线Py>> {
|
||
self.inner
|
||
.终结K线
|
||
.as_ref()
|
||
.map(|k| bar_to_py(py, Arc::clone(k)))
|
||
}
|
||
|
||
#[getter]
|
||
/// 破位值
|
||
fn 破位值(&self) -> f64 {
|
||
self.inner.破位值
|
||
}
|
||
|
||
#[getter]
|
||
fn 结构(&self) -> Option<crate::types_py::分型结构Py> {
|
||
self.inner
|
||
.结构
|
||
.map(|f| crate::types_py::分型结构Py { inner: f })
|
||
}
|
||
|
||
#[getter]
|
||
/// 偏移
|
||
fn 偏移(&self) -> i64 {
|
||
self.inner.偏移()
|
||
}
|
||
|
||
#[getter]
|
||
/// 失效偏移
|
||
fn 失效偏移(&self) -> i64 {
|
||
self.inner.失效偏移()
|
||
}
|
||
|
||
#[getter]
|
||
/// 有效性
|
||
fn 有效性(&self) -> bool {
|
||
self.inner.有效性()
|
||
}
|
||
|
||
#[getter]
|
||
/// 与MACD柱子匹配
|
||
fn 与MACD柱子匹配(&self) -> bool {
|
||
self.inner.与MACD柱子匹配()
|
||
}
|
||
|
||
#[getter]
|
||
/// 与MACD柱子分型匹配
|
||
fn 与MACD柱子分型匹配(&self) -> bool {
|
||
self.inner.与MACD柱子分型匹配()
|
||
}
|
||
|
||
/// pandas 兼容 — 返回关键标量字段构成的字典
|
||
#[getter]
|
||
fn __dict__(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
|
||
let dict = PyDict::new(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.有效性())?;
|
||
dict.set_item("与MACD柱子匹配", self.与MACD柱子匹配())?;
|
||
dict.set_item("与MACD柱子分型匹配", self.与MACD柱子分型匹配())?;
|
||
if let Some(v) = self.结构() {
|
||
dict.set_item("结构", v)?;
|
||
}
|
||
Ok(dict.into())
|
||
}
|
||
|
||
fn __str__(&self) -> String {
|
||
format!("{}", self.inner)
|
||
}
|
||
|
||
fn __repr__(&self) -> String {
|
||
self.__str__()
|
||
}
|
||
}
|
||
|
||
// ========== 买卖点 ==========
|
||
|
||
/// 买卖点 — 继承 基础买卖点,提供一二三类及扩展类型(T1/T1P/T2/T2S/T3A/T3B)的工厂类方法。
|
||
///
|
||
/// 类方法(均返回 买卖点 实例):
|
||
/// :meth:`一卖点` / :meth:`一买点` / :meth:`二卖点` / :meth:`二买点` / :meth:`三卖点` / :meth:`三买点`
|
||
/// :meth:`T1卖点` / :meth:`T1买点` / :meth:`T1P卖点` / :meth:`T1P买点`
|
||
/// :meth:`T2卖点` / :meth:`T2买点` / :meth:`T2S卖点` / :meth:`T2S买点`
|
||
/// :meth:`T3A卖点` / :meth:`T3A买点` / :meth:`T3B卖点` / :meth:`T3B买点`
|
||
/// :meth:`生成买卖点` — 根据特征和序号路由到对应构造函数
|
||
#[pyclass(name = "买卖点", module = "chanlun._chanlun", extends=基础买卖点Py)]
|
||
pub struct 买卖点Py;
|
||
|
||
#[pymethods]
|
||
impl 买卖点Py {
|
||
#[classmethod]
|
||
fn 一卖点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::一卖点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn 一买点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::一买点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn 二卖点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::二卖点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn 二买点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::二买点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn 三卖点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::三卖点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn 三买点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::三买点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn T1卖点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::T1卖点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn T1买点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::T1买点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn T1P卖点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::T1P卖点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn T1P买点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::T1P买点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn T2卖点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::T2卖点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn T2买点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::T2买点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn T2S卖点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::T2S卖点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn T2S买点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::T2S买点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn T3A卖点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::T3A卖点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn T3A买点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::T3A买点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn T3B卖点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::T3B卖点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
fn T3B买点(
|
||
_cls: &Bound<'_, PyType>,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前K线: &Bound<'_, K线Py>,
|
||
标识: &str,
|
||
备注: String,
|
||
中枢破位值: f64,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::T3B买点(
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
当前K线.borrow().inner.clone(),
|
||
标识,
|
||
备注,
|
||
中枢破位值,
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
|
||
#[classmethod]
|
||
#[pyo3(signature = (特征, 序号, 级别, 买卖点分型, 当前缠K))]
|
||
fn 生成买卖点(
|
||
_cls: &Bound<'_, PyType>,
|
||
特征: &str,
|
||
序号: &str,
|
||
级别: &str,
|
||
买卖点分型: &Bound<'_, 分型Py>,
|
||
当前缠K: &Bound<'_, 缠论K线Py>,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<Self>> {
|
||
let base = 基础买卖点Py {
|
||
inner: chanlun::business::bsp::买卖点::生成买卖点(
|
||
特征,
|
||
序号,
|
||
级别,
|
||
Arc::clone(&买卖点分型.borrow().inner),
|
||
Arc::clone(&当前缠K.borrow().inner),
|
||
),
|
||
};
|
||
let init = PyClassInitializer::from(base).add_subclass(买卖点Py);
|
||
Ok(Bound::new(py, init)?.unbind())
|
||
}
|
||
}
|
||
|
||
// ========== 观察者 ==========
|
||
|
||
/// 观察者 — 单周期分析器,接收 K 线流式输入后逐层计算所有序列。
|
||
///
|
||
/// 支持 Python 继承(__new__ + __init__ 构造函数)。每根 K 线喂入后
|
||
/// 自动增量计算: 缠论K线 → 分型 → 笔 → 线段 → 中枢 等全部层级。
|
||
///
|
||
/// 构造:
|
||
/// 观察者(符号, 周期, 配置) — 创建指定周期和配置的分析器
|
||
///
|
||
/// 属性(只读):
|
||
/// 符号: str / 周期: int / 标识: str
|
||
/// 当前K线: K线|None / 当前缠K: 缠论K线|None
|
||
///
|
||
/// 序列 getter(只读,返回 list 副本):
|
||
/// 普通K线序列 / 缠论K线序列 / 分型序列
|
||
/// 笔序列 / 笔_中枢序列
|
||
/// 线段序列 / 中枢序列
|
||
/// 扩展线段序列 / 扩展中枢序列
|
||
/// 扩展线段序列_线段 / 扩展中枢序列_线段
|
||
/// 线段_线段序列 / 线段_中枢序列
|
||
/// 扩展线段序列_扩展线段 / 扩展中枢序列_扩展线段
|
||
///
|
||
/// 分析层次(可读写):
|
||
/// 线段分析层次: int (默认 3) / 扩展线段分析层次: int (默认 3) / 混合扩展线段分析层次: int (默认 3)
|
||
///
|
||
/// 序列组(只读,返回 list[list[...]]):
|
||
/// 线段序列组 / 中枢序列组 / 扩展线段序列组 / 扩展中枢序列组
|
||
/// 混合扩展线段序列组 / 混合扩展中枢序列组
|
||
///
|
||
/// 核心方法:
|
||
/// 增加原始K线(普K) — 喂入一根普通K线,触发全层级增量计算
|
||
/// 重置基础序列() — 清空所有计算状态和序列
|
||
/// 静态重新分析() — 对已有普通K线序列进行全量重新分析
|
||
///
|
||
/// 调试方法:
|
||
/// 测试_保存数据(root?) — 将各层级序列保存到文件,用于与Python版对比
|
||
/// 读取数据文件(文件路径, 配置) -> 观察者 (classmethod) — 从 .nb 文件加载数据
|
||
#[pyclass(name = "观察者", module = "chanlun._chanlun", subclass)]
|
||
pub struct 观察者Py {
|
||
pub(crate) inner: Option<Arc<RwLock<chanlun::business::observer::观察者>>>,
|
||
配置缓存: std::sync::Mutex<Option<Py<缠论配置Py>>>,
|
||
}
|
||
|
||
impl 观察者Py {
|
||
pub(crate) fn obs(
|
||
&self,
|
||
) -> std::sync::RwLockReadGuard<'_, chanlun::business::observer::观察者> {
|
||
self.inner
|
||
.as_ref()
|
||
.expect("观察者 尚未初始化,请通过 __init__(符号, 周期, 配置) 构造")
|
||
.read()
|
||
.unwrap_or_else(|e| e.into_inner())
|
||
}
|
||
|
||
pub(crate) fn obs_mut(
|
||
&self,
|
||
) -> std::sync::RwLockWriteGuard<'_, chanlun::business::observer::观察者> {
|
||
self.inner
|
||
.as_ref()
|
||
.expect("观察者 尚未初始化,请通过 __init__(符号, 周期, 配置) 构造")
|
||
.write()
|
||
.unwrap_or_else(|e| e.into_inner())
|
||
}
|
||
}
|
||
|
||
#[pymethods]
|
||
impl 观察者Py {
|
||
/// __new__ 从 *args/**kwargs 中提取 (符号, 周期, 配置),完整初始化观察者。
|
||
/// 由于形参可变(子类参数各异),不依赖固定签名,而是从 args/kwargs 中按位置和名称智能提取。
|
||
#[new]
|
||
#[pyo3(signature = (*args, **kwargs))]
|
||
fn new(
|
||
args: &Bound<'_, pyo3::types::PyTuple>,
|
||
kwargs: Option<&Bound<'_, pyo3::types::PyDict>>,
|
||
) -> PyResult<Self> {
|
||
let py = args.py();
|
||
|
||
// 提取 符号(位置 0 或关键字)
|
||
let 符号: String = if !args.is_empty() {
|
||
args.get_item(0)?.extract()?
|
||
} else {
|
||
match kwargs.and_then(|kw| kw.get_item("符号").ok().flatten()) {
|
||
Some(val) => val.extract()?,
|
||
None => return Err(pyo3::exceptions::PyTypeError::new_err("缺少参数: 符号")),
|
||
}
|
||
};
|
||
|
||
// 提取 周期(位置 1 或关键字)
|
||
let 周期: i64 = if args.len() >= 2 {
|
||
args.get_item(1)?.extract()?
|
||
} else {
|
||
match kwargs.and_then(|kw| kw.get_item("周期").ok().flatten()) {
|
||
Some(val) => val.extract()?,
|
||
None => return Err(pyo3::exceptions::PyTypeError::new_err("缺少参数: 周期")),
|
||
}
|
||
};
|
||
|
||
// 提取 配置(关键字优先,然后扫描剩余位置参数)
|
||
let mut config = None;
|
||
if let Some(val) = kwargs.and_then(|kw| kw.get_item("配置").ok().flatten()) {
|
||
let cfg: PyRef<'_, 缠论配置Py> = val.extract()?;
|
||
config = Some(cfg.to_rust_config(py)?);
|
||
}
|
||
if config.is_none() {
|
||
for i in 2..args.len() {
|
||
if let Ok(cfg) = args.get_item(i)?.extract::<PyRef<'_, 缠论配置Py>>() {
|
||
config = Some(cfg.to_rust_config(py)?);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
let config = config.unwrap_or_else(chanlun::config::缠论配置::default);
|
||
|
||
Ok(Self {
|
||
inner: Some(chanlun::business::observer::观察者::new(
|
||
符号, 周期, config,
|
||
)),
|
||
配置缓存: std::sync::Mutex::new(None),
|
||
})
|
||
}
|
||
|
||
/// __init__ 不重复构造 — __new__ 已完成初始化。
|
||
/// 接收 *args/**kwargs 是为了兼容 Python 构造函数给 __init__ 传入的任意形参。
|
||
#[pyo3(signature = (*args, **kwargs))]
|
||
fn __init__(
|
||
&mut self,
|
||
args: &Bound<'_, pyo3::types::PyTuple>,
|
||
kwargs: Option<&Bound<'_, pyo3::types::PyDict>>,
|
||
) -> PyResult<()> {
|
||
let _ = (args, kwargs);
|
||
Ok(())
|
||
}
|
||
|
||
#[getter]
|
||
/// 观察员(自引用)
|
||
fn 观察员(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
|
||
slf
|
||
}
|
||
|
||
#[getter]
|
||
/// :return: "{符号}:{周期}"
|
||
fn 标识(&self) -> String {
|
||
self.obs().标识()
|
||
}
|
||
|
||
#[getter]
|
||
/// :return: 最后一根原始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, py: Python<'_>) -> Option<Py<缠论K线Py>> {
|
||
self.obs()
|
||
.当前缠K()
|
||
.map(|k| crate::kline_py::chan_kline_to_py(py, Arc::clone(k)))
|
||
}
|
||
|
||
#[getter]
|
||
fn 符号(&self) -> String {
|
||
self.obs().符号.clone()
|
||
}
|
||
|
||
#[getter]
|
||
fn 周期(&self) -> i64 {
|
||
self.obs().周期
|
||
}
|
||
|
||
#[getter]
|
||
fn 配置(&self, py: Python<'_>) -> PyResult<Py<缠论配置Py>> {
|
||
let mut cache = self.配置缓存.lock().unwrap();
|
||
if let Some(ref cached) = *cache {
|
||
Ok(cached.clone_ref(py))
|
||
} else {
|
||
let cfg_py = 缠论配置Py::from_rust_config(&self.obs().配置)?;
|
||
let obj = Py::new(py, cfg_py)?;
|
||
*cache = Some(obj.clone_ref(py));
|
||
Ok(obj)
|
||
}
|
||
}
|
||
|
||
#[setter]
|
||
fn set_配置(&self, value: &Bound<'_, 缠论配置Py>) -> PyResult<()> {
|
||
let config = value.borrow().to_rust_config(value.py())?;
|
||
self.obs_mut().配置 = config;
|
||
self.配置缓存
|
||
.lock()
|
||
.unwrap()
|
||
.replace(value.clone().unbind());
|
||
Ok(())
|
||
}
|
||
|
||
/// 清空所有分析序列,重置为初始状态(内部实现)
|
||
#[pyo3(name = "_重置基础序列")]
|
||
fn 重置基础序列_impl(&mut self) -> PyResult<()> {
|
||
self.obs_mut().重置基础序列();
|
||
Ok(())
|
||
}
|
||
|
||
/// 清空所有分析序列,重置为初始状态(公开分发器,支持子类重写)
|
||
fn 重置基础序列(slf: &Bound<'_, Self>) -> PyResult<()> {
|
||
slf.call_method1("_重置基础序列", ())?;
|
||
Ok(())
|
||
}
|
||
|
||
/// 核心入口 — 投喂一根原始K线,增量更新所有层级(内部实现)
|
||
#[pyo3(name = "_增加原始K线")]
|
||
fn 增加原始K线_impl(&mut self, 普K: &Bound<'_, K线Py>) -> PyResult<()> {
|
||
self.obs_mut().增加原始K线((*普K.borrow().inner).clone());
|
||
Ok(())
|
||
}
|
||
|
||
/// 核心入口 — 投喂一根原始K线,增量更新所有层级(公开分发器,支持子类重写)
|
||
fn 增加原始K线(slf: &Bound<'_, Self>, 普K: &Bound<'_, K线Py>) -> PyResult<()> {
|
||
// 同步缓存的 Python 配置到 Rust 观察者(支持 obs.配置 直接修改)
|
||
{
|
||
let me = slf.borrow();
|
||
if let Some(ref cached) = *me.配置缓存.lock().unwrap() {
|
||
let py = slf.py();
|
||
if let Ok(config) = cached.bind(py).borrow().to_rust_config(py) {
|
||
me.obs_mut().配置 = config;
|
||
}
|
||
}
|
||
}
|
||
slf.call_method1("_增加原始K线", (普K,))?;
|
||
Ok(())
|
||
}
|
||
|
||
/// 投喂原始数据 — 便捷入口,直接从 OHLCV 创建 K线 并通过 Python 分发 增加原始K线,
|
||
/// 确保子类重写的 增加原始K线 被正确调用。
|
||
fn 投喂原始数据(
|
||
slf: &Bound<'_, Self>,
|
||
时间戳: i64,
|
||
开: f64,
|
||
高: f64,
|
||
低: f64,
|
||
收: f64,
|
||
量: f64,
|
||
) -> PyResult<()> {
|
||
let (符号, 周期) = {
|
||
let me = slf.borrow();
|
||
let obs = me.obs();
|
||
(obs.符号.clone(), obs.周期)
|
||
};
|
||
let kline = bar_to_py(
|
||
slf.py(),
|
||
Arc::new(chanlun::kline::bar::K线::创建普K(
|
||
&符号, 时间戳, 开, 高, 低, 收, 量, 0, 周期,
|
||
)),
|
||
);
|
||
slf.call_method1("增加原始K线", (kline,))?;
|
||
Ok(())
|
||
}
|
||
|
||
/// 加载本地数据 — 从 .nb 文件加载K线数据(先重置,再通过 Python dispatch 逐根投喂,
|
||
/// 确保子类重写的 增加原始K线 被正确调用)。
|
||
fn 加载本地数据(slf: &Bound<'_, Self>, 文件路径: &str) -> PyResult<()> {
|
||
// 重置基础序列(通过 Python 分发,支持子类重写)
|
||
slf.call_method1("重置基础序列", ())?;
|
||
|
||
// 读取文件,通过 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(())
|
||
}
|
||
|
||
/// 静态重新分析(内部实现)
|
||
#[pyo3(name = "_静态重新分析")]
|
||
fn 静态重新分析_impl(&mut self) -> PyResult<()> {
|
||
self.obs_mut().静态重新分析();
|
||
Ok(())
|
||
}
|
||
|
||
/// 静态重新分析(公开分发器,支持子类重写)
|
||
fn 静态重新分析(slf: &Bound<'_, Self>) -> PyResult<()> {
|
||
slf.call_method1("_静态重新分析", ())?;
|
||
Ok(())
|
||
}
|
||
|
||
/// 拆分各序列数据,单独存文件,文件名为对应变量名(内部实现)
|
||
#[pyo3(name = "_测试_保存数据", signature = (root = None))]
|
||
fn 测试_保存数据_impl(&self, root: Option<&str>) -> String {
|
||
self.obs().测试_保存数据(root)
|
||
}
|
||
|
||
/// 拆分各序列数据,单独存文件,文件名为对应变量名(公开分发器,支持子类重写)
|
||
#[pyo3(signature = (root = None))]
|
||
fn 测试_保存数据(slf: &Bound<'_, Self>, root: Option<&str>) -> PyResult<String> {
|
||
let result = slf.call_method1("_测试_保存数据", (root,))?;
|
||
result.extract::<String>()
|
||
}
|
||
|
||
#[classmethod]
|
||
#[pyo3(signature = (文件路径, 配置 = None, 观察员 = None))]
|
||
/// :param 文件路径: 数据文件路径 格式如: btcusd-300-1631772074-1632222374.nb
|
||
/// :param 配置: 缠论配置
|
||
/// :param 观察员: 可选,已有观察者实例;不传则自动创建
|
||
/// :return: 观察者实例
|
||
fn 读取数据文件(
|
||
_cls: &Bound<'_, PyType>,
|
||
文件路径: &str,
|
||
配置: Option<&Bound<'_, 缠论配置Py>>,
|
||
观察员: Option<&Bound<'_, Self>>,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<PyAny>> {
|
||
let config = match 配置 {
|
||
Some(cfg) => cfg.borrow().to_rust_config(py)?,
|
||
None => chanlun::config::缠论配置::default(),
|
||
};
|
||
|
||
// 从文件名解析 符号/周期: "btcusd-300-1761327300-1776327900.nb"
|
||
let path = std::path::Path::new(文件路径);
|
||
let name = path
|
||
.file_stem()
|
||
.and_then(|n| n.to_str())
|
||
.ok_or_else(|| pyo3::exceptions::PyValueError::new_err("invalid filename"))?;
|
||
let parts: Vec<&str> = name.split('-').collect();
|
||
if parts.len() < 4 {
|
||
return Err(pyo3::exceptions::PyValueError::new_err(format!(
|
||
"invalid filename format: {}",
|
||
name
|
||
)));
|
||
}
|
||
let 符号 = parts[0].to_string();
|
||
let 周期: i64 = parts[1]
|
||
.parse()
|
||
.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("parse period: {}", e)))?;
|
||
|
||
let obs_ref = match 观察员 {
|
||
Some(obs) => {
|
||
// 更新已有观察员属性
|
||
{
|
||
let slf_ref = obs.borrow_mut();
|
||
let mut obs_mut = slf_ref.obs_mut();
|
||
obs_mut.符号 = 符号;
|
||
obs_mut.周期 = 周期;
|
||
obs_mut.配置 = config;
|
||
}
|
||
obs.clone()
|
||
}
|
||
None => {
|
||
// 创建新观察者:调用 cls(符号, 周期),配置后续通过 obs_mut 设置
|
||
let obj = _cls.call1((符号.as_str(), 周期))?;
|
||
let obs: &Bound<'_, Self> = obj.cast().map_err(|_| {
|
||
pyo3::exceptions::PyTypeError::new_err("failed to create 观察者")
|
||
})?;
|
||
{
|
||
let slf_ref = obs.borrow_mut();
|
||
let mut obs_mut = slf_ref.obs_mut();
|
||
obs_mut.配置 = config;
|
||
}
|
||
obs.clone()
|
||
}
|
||
};
|
||
|
||
// 调用加载本地数据(通过 Python dispatch,支持子类重写)
|
||
obs_ref.call_method1("加载本地数据", (文件路径,))?;
|
||
|
||
Ok(obs_ref.unbind().into())
|
||
}
|
||
|
||
// ---- 序列 getters ----
|
||
|
||
#[getter]
|
||
fn 普通K线序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
let list = pyo3::types::PyList::empty(py);
|
||
for k in &self.obs().普通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(crate::kline_py::chan_kline_to_py(py, Arc::clone(k)))?;
|
||
}
|
||
Ok(list.into())
|
||
}
|
||
|
||
#[getter]
|
||
fn 分型序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
let list = pyo3::types::PyList::empty(py);
|
||
for f in &self.obs().分型序列 {
|
||
list.append(fractal_to_py(py, Arc::clone(f)))?;
|
||
}
|
||
Ok(list.into())
|
||
}
|
||
|
||
#[getter]
|
||
fn 笔序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
let list = pyo3::types::PyList::empty(py);
|
||
for d in &self.obs().笔序列 {
|
||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||
}
|
||
Ok(list.into())
|
||
}
|
||
|
||
#[getter]
|
||
fn 笔_中枢序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
let list = pyo3::types::PyList::empty(py);
|
||
for h in &self.obs().笔_中枢序列 {
|
||
list.append(hub_to_py(py, Arc::clone(h)))?;
|
||
}
|
||
Ok(list.into())
|
||
}
|
||
|
||
#[getter]
|
||
fn 线段序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
let list = pyo3::types::PyList::empty(py);
|
||
for d in self.obs().线段序列() {
|
||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||
}
|
||
Ok(list.into())
|
||
}
|
||
|
||
#[getter]
|
||
fn 中枢序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
let list = pyo3::types::PyList::empty(py);
|
||
for h in self.obs().中枢序列() {
|
||
list.append(hub_to_py(py, Arc::clone(h)))?;
|
||
}
|
||
Ok(list.into())
|
||
}
|
||
|
||
#[getter]
|
||
fn 扩展线段序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
let list = pyo3::types::PyList::empty(py);
|
||
for d in self.obs().扩展线段序列() {
|
||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||
}
|
||
Ok(list.into())
|
||
}
|
||
|
||
#[getter]
|
||
fn 扩展中枢序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
let list = pyo3::types::PyList::empty(py);
|
||
for h in self.obs().扩展中枢序列() {
|
||
list.append(hub_to_py(py, Arc::clone(h)))?;
|
||
}
|
||
Ok(list.into())
|
||
}
|
||
|
||
#[getter]
|
||
fn 扩展线段序列_线段(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
let list = pyo3::types::PyList::empty(py);
|
||
for d in self.obs().扩展线段序列_线段() {
|
||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||
}
|
||
Ok(list.into())
|
||
}
|
||
|
||
#[getter]
|
||
fn 扩展中枢序列_线段(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
let list = pyo3::types::PyList::empty(py);
|
||
for h in self.obs().扩展中枢序列_线段() {
|
||
list.append(hub_to_py(py, Arc::clone(h)))?;
|
||
}
|
||
Ok(list.into())
|
||
}
|
||
|
||
#[getter]
|
||
fn 线段_线段序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
let list = pyo3::types::PyList::empty(py);
|
||
for d in self.obs().线段_线段序列() {
|
||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||
}
|
||
Ok(list.into())
|
||
}
|
||
|
||
#[getter]
|
||
fn 线段_中枢序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
let list = pyo3::types::PyList::empty(py);
|
||
for h in self.obs().线段_中枢序列() {
|
||
list.append(hub_to_py(py, Arc::clone(h)))?;
|
||
}
|
||
Ok(list.into())
|
||
}
|
||
|
||
#[getter]
|
||
fn 扩展线段序列_扩展线段(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
let list = pyo3::types::PyList::empty(py);
|
||
for d in self.obs().扩展线段序列_扩展线段() {
|
||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||
}
|
||
Ok(list.into())
|
||
}
|
||
|
||
#[getter]
|
||
fn 扩展中枢序列_扩展线段(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
let list = pyo3::types::PyList::empty(py);
|
||
for h in self.obs().扩展中枢序列_扩展线段() {
|
||
list.append(hub_to_py(py, Arc::clone(h)))?;
|
||
}
|
||
Ok(list.into())
|
||
}
|
||
|
||
// ---- 分析层次 getter/setter ----
|
||
|
||
#[getter]
|
||
fn 线段分析层次(&self) -> usize {
|
||
self.obs().线段分析层次
|
||
}
|
||
|
||
#[setter]
|
||
#[pyo3(name = "线段分析层次")]
|
||
fn 设置_线段分析层次(&mut self, value: usize) {
|
||
self.obs_mut().线段分析层次 = value;
|
||
}
|
||
|
||
#[getter]
|
||
fn 扩展线段分析层次(&self) -> usize {
|
||
self.obs().扩展线段分析层次
|
||
}
|
||
|
||
#[setter]
|
||
#[pyo3(name = "扩展线段分析层次")]
|
||
fn 设置_扩展线段分析层次(&mut self, value: usize) {
|
||
self.obs_mut().扩展线段分析层次 = value;
|
||
}
|
||
|
||
#[getter]
|
||
fn 混合扩展线段分析层次(&self) -> usize {
|
||
self.obs().混合扩展线段分析层次
|
||
}
|
||
|
||
#[setter]
|
||
#[pyo3(name = "混合扩展线段分析层次")]
|
||
fn 设置_混合扩展线段分析层次(&mut self, value: usize) {
|
||
self.obs_mut().混合扩展线段分析层次 = value;
|
||
}
|
||
|
||
// ---- 序列组 getters (返回 list[list[...]]) ----
|
||
|
||
#[getter]
|
||
fn 线段序列组(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
self._虚线序列组_to_py(&self.obs().线段序列组, py)
|
||
}
|
||
|
||
#[getter]
|
||
fn 中枢序列组(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
self._中枢序列组_to_py(&self.obs().中枢序列组, py)
|
||
}
|
||
|
||
#[getter]
|
||
fn 扩展线段序列组(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
self._虚线序列组_to_py(&self.obs().扩展线段序列组, py)
|
||
}
|
||
|
||
#[getter]
|
||
fn 扩展中枢序列组(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
self._中枢序列组_to_py(&self.obs().扩展中枢序列组, py)
|
||
}
|
||
|
||
#[getter]
|
||
fn 混合扩展线段序列组(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
self._虚线序列组_to_py(&self.obs().混合扩展线段序列组, py)
|
||
}
|
||
|
||
#[getter]
|
||
fn 混合扩展中枢序列组(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
self._中枢序列组_to_py(&self.obs().混合扩展中枢序列组, py)
|
||
}
|
||
}
|
||
|
||
impl 观察者Py {
|
||
fn _虚线序列组_to_py(
|
||
&self,
|
||
组: &Vec<Vec<Arc<chanlun::structure::dash_line::虚线>>>,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<PyAny>> {
|
||
let outer = pyo3::types::PyList::empty(py);
|
||
for inner_vec in 组 {
|
||
let inner_list = pyo3::types::PyList::empty(py);
|
||
for d in inner_vec {
|
||
inner_list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||
}
|
||
outer.append(inner_list)?;
|
||
}
|
||
Ok(outer.into())
|
||
}
|
||
|
||
fn _中枢序列组_to_py(
|
||
&self,
|
||
组: &Vec<Vec<Arc<chanlun::algorithm::hub::中枢>>>,
|
||
py: Python<'_>,
|
||
) -> PyResult<Py<PyAny>> {
|
||
let outer = pyo3::types::PyList::empty(py);
|
||
for inner_vec in 组 {
|
||
let inner_list = pyo3::types::PyList::empty(py);
|
||
for h in inner_vec {
|
||
inner_list.append(hub_to_py(py, Arc::clone(h)))?;
|
||
}
|
||
outer.append(inner_list)?;
|
||
}
|
||
Ok(outer.into())
|
||
}
|
||
}
|
||
|
||
// ========== K线合成器 ==========
|
||
|
||
/// K线合成器 — 将小周期K线合成为大周期K线。
|
||
///
|
||
/// 构造:
|
||
/// K线合成器(标识, 周期组) — 周期组为升序排列的整数列表(如 [60, 300, 900])
|
||
///
|
||
/// 方法:
|
||
/// 投喂K线(普K) — 喂入普通K线,完成事件通过回调分发
|
||
/// 投喂(时间戳, 开盘价, 最高价, 最低价, 收盘价, 成交量) — 快捷入口
|
||
/// 获取当前K线(周期) -> K线|None — 获取指定周期的当前合成结果
|
||
#[pyclass(name = "K线合成器", module = "chanlun._chanlun")]
|
||
pub struct K线合成器Py {
|
||
pub(crate) inner: chanlun::business::synthesizer::K线合成器,
|
||
}
|
||
|
||
#[pymethods]
|
||
impl K线合成器Py {
|
||
#[new]
|
||
fn new(标识: String, 周期组: Vec<i64>) -> Self {
|
||
Self {
|
||
inner: chanlun::business::synthesizer::K线合成器::new(标识, 周期组, None),
|
||
}
|
||
}
|
||
|
||
/// 投喂K线 — 输入最小周期K线,合成为所有目标周期,完成事件通过回调分发
|
||
fn 投喂K线(&mut self, 普K: &Bound<'_, K线Py>) {
|
||
self.inner.投喂K线((*普K.borrow().inner).clone());
|
||
}
|
||
|
||
/// 投喂原始tick数据
|
||
fn 投喂(&mut self, 时间戳: i64, 开: f64, 高: f64, 低: f64, 收: f64, 量: f64) {
|
||
self.inner.投喂(时间戳, 开, 高, 低, 收, 量);
|
||
}
|
||
|
||
/// 获取指定周期当前正在合成的K线
|
||
fn 获取当前K线(&self, 周期: i64, py: Python<'_>) -> Option<Py<K线Py>> {
|
||
self.inner
|
||
.获取当前K线(周期)
|
||
.map(|k| bar_to_py(py, Arc::new(k.clone())))
|
||
}
|
||
|
||
#[getter]
|
||
fn 标识(&self) -> String {
|
||
self.inner.标识.clone()
|
||
}
|
||
|
||
#[getter]
|
||
fn 周期组(&self) -> Vec<i64> {
|
||
self.inner.周期组.clone()
|
||
}
|
||
}
|
||
|
||
// ========== 立体分析器 ==========
|
||
|
||
/// 立体分析器 — 多周期分析器,内部包含 K线合成器 + 每周期一个观察者。
|
||
///
|
||
/// 通过最小周期数据合成大周期,并将各周期分析结果对齐。
|
||
///
|
||
/// 构造:
|
||
/// 立体分析器(符号, 周期组, 配置?, 配置组?) — 周期组为升序列表(如 [60, 300, 900])
|
||
/// 配置为默认配置,配置组为各周期独立配置(dict[周期, 缠论配置])
|
||
///
|
||
/// 方法:
|
||
/// 投喂K线(普K) — 喂入最小周期K线,自动合成大周期并分发给各周期观察者
|
||
/// 测试_保存数据(root?) — 保存各周期的分析数据到文件
|
||
#[pyclass(name = "立体分析器", module = "chanlun._chanlun")]
|
||
pub struct 立体分析器Py {
|
||
pub(crate) inner: chanlun::business::multi_frame::立体分析器,
|
||
}
|
||
|
||
#[pymethods]
|
||
impl 立体分析器Py {
|
||
#[new]
|
||
#[pyo3(signature = (符号, 周期组, 配置 = None, 配置组 = None))]
|
||
fn new(
|
||
符号: String,
|
||
周期组: Vec<i64>,
|
||
配置: Option<&Bound<'_, 缠论配置Py>>,
|
||
配置组: Option<&Bound<'_, PyAny>>,
|
||
py: Python<'_>,
|
||
) -> PyResult<Self> {
|
||
let config = match 配置 {
|
||
Some(cfg) => Some(cfg.borrow().to_rust_config(py)?),
|
||
None => None,
|
||
};
|
||
let cfg_map: Option<HashMap<i64, chanlun::config::缠论配置>> = match 配置组 {
|
||
Some(dict_any) => {
|
||
let dict = dict_any.cast::<pyo3::types::PyDict>()?;
|
||
let mut map = HashMap::new();
|
||
for (key, value) in dict.iter() {
|
||
let period: i64 = key.extract()?;
|
||
let cfg: PyRef<'_, 缠论配置Py> = value.extract()?;
|
||
map.insert(period, cfg.to_rust_config(py)?);
|
||
}
|
||
Some(map)
|
||
}
|
||
None => None,
|
||
};
|
||
Ok(Self {
|
||
inner: chanlun::business::multi_frame::立体分析器::new(
|
||
符号, 周期组, config, cfg_map,
|
||
),
|
||
})
|
||
}
|
||
|
||
/// 统一入口 — 投喂最小周期K线,自动合成大周期并分发给各周期观察者
|
||
fn 投喂K线(&mut self, 普K: &Bound<'_, K线Py>) {
|
||
self.inner.投喂K线((*普K.borrow().inner).clone());
|
||
}
|
||
|
||
/// 拆分各序列数据,单独存文件,文件名为对应变量名
|
||
fn 测试_保存数据(&self, root: Option<&str>) {
|
||
self.inner.测试_保存数据(root);
|
||
}
|
||
|
||
#[getter]
|
||
fn 周期组(&self) -> Vec<i64> {
|
||
self.inner.周期组.clone()
|
||
}
|
||
|
||
/// _单体分析器 — 对应 Python 立体分析器._单体分析器: dict[周期, 观察者]
|
||
#[getter(_单体分析器)]
|
||
fn get_单体分析器(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||
let dict = pyo3::types::PyDict::new(py);
|
||
for (周期, obs_rc) in &self.inner.单体分析器 {
|
||
let obs_py = 观察者Py {
|
||
inner: Some(obs_rc.clone()),
|
||
配置缓存: std::sync::Mutex::new(None),
|
||
};
|
||
dict.set_item(周期, obs_py)?;
|
||
}
|
||
Ok(dict.into())
|
||
}
|
||
}
|
||
|
||
pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||
m.add_class::<基础买卖点Py>()?;
|
||
m.add_class::<买卖点Py>()?;
|
||
m.add_class::<观察者Py>()?;
|
||
m.add_class::<K线合成器Py>()?;
|
||
m.add_class::<立体分析器Py>()?;
|
||
Ok(())
|
||
}
|