第三版

This commit is contained in:
YuWuKunCheng
2026-05-25 12:41:14 +08:00
parent bf96517c9d
commit 7416bfc73a
17 changed files with 4622 additions and 1 deletions
+5
View File
@@ -0,0 +1,5 @@
target/
__pycache__/
*.egg-info/
dist/
build/
+20
View File
@@ -0,0 +1,20 @@
[package]
name = "chanlun-py"
version = "26.5.1"
edition = "2021"
description = "缠论技术分析库 — Rust 高性能 Python 绑定"
authors = ["YuYuKunKun"]
license = "MIT"
repository = "https://github.com/YuYuKunKun/chanlun.rs"
[lib]
crate-type = ["cdylib"]
name = "chanlun"
[dependencies]
# 发布至 PyPI 前,需先将 chanlun 发布至 crates.io,然后替换为版本号依赖:
# chanlun = "0.1"
chanlun = { path = "../chanlun" }
pyo3 = { version = "0.28", features = ["extension-module"] }
serde_json = "1"
chrono = "0.4"
+1
View File
@@ -0,0 +1 @@
../LICENSE
+1
View File
@@ -0,0 +1 @@
../README.md
+163
View File
@@ -0,0 +1,163 @@
#!/usr/bin/env bash
# ============================================================
# chanlun-py — 构建、安装、发布脚本
#
# 前置依赖:
# pip install maturin
# ============================================================
set -euo pipefail
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$PROJECT_DIR"
# 颜色输出
red() { echo -e "\033[31m$*\033[0m"; }
green() { echo -e "\033[32m$*\033[0m"; }
yellow() { echo -e "\033[33m$*\033[0m"; }
usage() {
cat <<'EOF'
用法: ./build.sh <命令>
命令:
build 开发模式编译(debug)
release 发布模式编译(release,带优化)
install 构建 wheel 并安装到当前 Python 环境
install-rel 同 install
develop 开发模式安装到 venv(需要激活虚拟环境)
test 运行集成测试
sdist 构建源码分发包
wheel 构建 wheel 包(release
publish 发布至 PyPI(需先配置 TWINE_ 环境变量或 .pypirc
clean 清理构建产物
check 检查 pyproject.toml 配置是否合法
PyPI 发布流程:
1. 确认 Cargo.toml 中 chanlun 依赖已切换为 crates.io 版本(非 path
2. 更新版本号: pyproject.toml + Cargo.toml
3. ./build.sh check — 验证配置
4. ./build.sh publish — 构建并推送至 PyPI
环境变量:
TWINE_USERNAME — PyPI 用户名(或 __token__ 使用 API token
TWINE_PASSWORD — PyPI 密码或 API token
或通过 twine 的 ~/.pypirc 配置
EOF
}
cmd_build() {
green "[build] 开发模式编译..."
cargo build
green "编译完成。使用 'maturin develop' 安装到 venv,或 './build.sh wheel' 构建 wheel"
}
cmd_release() {
green "[release] 发布模式编译..."
cargo build --release
}
cmd_install() {
green "[install] 构建 wheel 并安装..."
maturin build --release 2>&1 | tail -3
local wheel=$(ls -t target/wheels/*.whl 2>/dev/null | head -1)
if [ -z "$wheel" ]; then
red "未找到 wheel 文件"
exit 1
fi
pip install --force-reinstall "$wheel" 2>&1 | tail -3
green "安装完成!"
}
cmd_install_rel() {
cmd_install
}
cmd_develop() {
green "[develop] 开发模式安装(需要 venv..."
maturin develop 2>&1 | tail -3
}
cmd_test() {
green "[test] 运行集成测试..."
# 确保已安装
python3 -c "import chanlun" 2>/dev/null || {
yellow "chanlun 未安装,正在构建并安装..."
maturin build --release 2>&1 | tail -3
local wheel=$(ls -t target/wheels/*.whl 2>/dev/null | head -1)
pip install --force-reinstall "$wheel" 2>&1 | tail -3
}
python3 test_integration.py
}
cmd_sdist() {
green "[sdist] 构建源码分发包..."
maturin sdist
yellow "构建产物在: target/wheels/"
ls -lh target/wheels/*.tar.gz 2>/dev/null || true
}
cmd_wheel() {
green "[wheel] 构建 wheel 包..."
maturin build --release
yellow "构建产物在: target/wheels/"
ls -lh target/wheels/*.whl 2>/dev/null || true
}
cmd_publish() {
yellow "发布前请确认:"
yellow " 1. Cargo.toml 中 chanlun 依赖已切换为 crates.io 版本"
yellow " 2. 版本号已更新 (pyproject.toml + Cargo.toml)"
yellow " 3. git 工作区干净且已打 tag"
echo ""
read -rp "确认发布至 PyPI? [y/N] " confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
red "已取消"
exit 0
fi
green "[publish] 构建并发布至 PyPI..."
maturin publish
green "发布完成!"
yellow "安装: pip install chanlun"
}
cmd_clean() {
green "[clean] 清理构建产物..."
cargo clean
rm -rf target/wheels/ dist/ build/ *.egg-info/
find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
green "清理完成"
}
cmd_check() {
green "[check] 验证 pyproject.toml..."
python3 -c "
import tomllib
with open('pyproject.toml', 'rb') as f:
data = tomllib.load(f)
print(' name:', data['project']['name'])
print(' version:', data['project']['version'])
print(' requires-python:', data['project']['requires-python'])
print(' build-backend:', data['build-system']['build-backend'])
print(' OK')
"
green "配置验证通过"
}
# --- main ---
case "${1:-}" in
build) cmd_build ;;
release) cmd_release ;;
install) cmd_install ;;
install-rel) cmd_install_rel ;;
develop) cmd_develop ;;
test) cmd_test ;;
sdist) cmd_sdist ;;
wheel) cmd_wheel ;;
publish) cmd_publish ;;
clean) cmd_clean ;;
check) cmd_check ;;
-h|--help|help) usage ;;
*) red "未知命令: ${1:-}"; usage; exit 1 ;;
esac
+3
View File
@@ -0,0 +1,3 @@
"""缠论技术分析库 — Rust 高性能实现"""
from ._chanlun import *
+39
View File
@@ -0,0 +1,39 @@
[build-system]
requires = ["maturin>=1.0,<2.0"]
build-backend = "maturin"
[project]
name = "chanlun"
version = "2605.1"
description = "缠论技术分析库 — Rust 高性能实现"
readme = { file = "README.md", content-type = "text/markdown" }
license = { file = "LICENSE", content-type = "text/plain" }
authors = [
{ name = "YuYuKunKun" },
]
keywords = ["chanlun", "technical-analysis", "trading", "stock", "crypto"]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Financial and Insurance Industry",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Rust",
"Topic :: Office/Business :: Financial :: Investment",
]
requires-python = ">=3.9"
[project.urls]
Homepage = "https://github.com/YuYuKunKun/chanlun.rs"
Repository = "https://github.com/YuYuKunKun/chanlun.rs"
Issues = "https://github.com/YuYuKunKun/chanlun.rs/issues"
[tool.maturin]
features = ["pyo3/extension-module"]
python-source = "."
module-name = "chanlun._chanlun"
manifest-path = "Cargo.toml"
File diff suppressed because it is too large Load Diff
+579
View File
@@ -0,0 +1,579 @@
use pyo3::prelude::*;
use pyo3::types::PyType;
use std::collections::HashMap;
use std::rc::Rc;
use crate::algorithm_py::Py;
use crate::config_py::Py;
use crate::kline_py::{K线Py, K线Py};
use crate::structure_py::{Py, 线Py};
use crate::types_py::Py;
// ========== 基础买卖点 ==========
#[pyclass(name = "基础买卖点", unsendable)]
#[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,
Rc::new(K线.borrow().inner.clone()),
Rc::clone(&.borrow().inner),
,
,
),
}
}
#[getter]
fn (&self) -> String {
self.inner..clone()
}
#[getter]
fn (&self) -> Py {
Py {
inner: self.inner.,
}
}
#[getter]
fn (&self) -> Py {
Py {
inner: Rc::clone(&self.inner.),
}
}
#[getter]
fn K线(&self) -> K线Py {
K线Py {
inner: Rc::clone(&self.inner.K线),
}
}
#[getter]
fn K线(&self) -> K线Py {
K线Py {
inner: (*self.inner.K线).clone(),
}
}
#[getter]
fn K线(&self) -> Option<K线Py> {
self.inner.K线.as_ref().map(|k| K线Py {
inner: (**k).clone(),
})
}
#[getter]
fn K线(&self) -> Option<K线Py> {
self.inner.K线.as_ref().map(|k| K线Py {
inner: (**k).clone(),
})
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[getter]
fn (&self) -> Option<crate::types_py::Py> {
self.inner
.
.map(|f| crate::types_py::Py { inner: f })
}
fn (&self) -> i64 {
self.inner.()
}
fn (&self) -> i64 {
self.inner.()
}
#[getter]
fn (&self) -> bool {
self.inner.()
}
#[getter]
fn MACD柱子匹配(&self) -> bool {
self.inner.MACD柱子匹配()
}
#[getter]
fn MACD柱子分型匹配(&self) -> bool {
self.inner.MACD柱子分型匹配()
}
fn __str__(&self) -> String {
format!("{}", self.inner)
}
fn __repr__(&self) -> String {
self.__str__()
}
}
// ========== 买卖点 ==========
#[pyclass(name = "买卖点")]
pub struct Py;
#[pymethods]
impl Py {
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, Py>,
K线: &Bound<'_, K线Py>,
: &str,
: String,
: f64,
) -> Py {
Py {
inner: chanlun::business::bsp::::(
Rc::clone(&.borrow().inner),
Rc::new(K线.borrow().inner.clone()),
,
,
,
),
}
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, Py>,
K线: &Bound<'_, K线Py>,
: &str,
: String,
: f64,
) -> Py {
Py {
inner: chanlun::business::bsp::::(
Rc::clone(&.borrow().inner),
Rc::new(K线.borrow().inner.clone()),
,
,
,
),
}
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, Py>,
K线: &Bound<'_, K线Py>,
: &str,
: String,
: f64,
) -> Py {
Py {
inner: chanlun::business::bsp::::(
Rc::clone(&.borrow().inner),
Rc::new(K线.borrow().inner.clone()),
,
,
,
),
}
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, Py>,
K线: &Bound<'_, K线Py>,
: &str,
: String,
: f64,
) -> Py {
Py {
inner: chanlun::business::bsp::::(
Rc::clone(&.borrow().inner),
Rc::new(K线.borrow().inner.clone()),
,
,
,
),
}
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, Py>,
K线: &Bound<'_, K线Py>,
: &str,
: String,
: f64,
) -> Py {
Py {
inner: chanlun::business::bsp::::(
Rc::clone(&.borrow().inner),
Rc::new(K线.borrow().inner.clone()),
,
,
,
),
}
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, Py>,
K线: &Bound<'_, K线Py>,
: &str,
: String,
: f64,
) -> Py {
Py {
inner: chanlun::business::bsp::::(
Rc::clone(&.borrow().inner),
Rc::new(K线.borrow().inner.clone()),
,
,
,
),
}
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
: &str,
: &str,
: &str,
: &Bound<'_, Py>,
K: &Bound<'_, K线Py>,
) -> Py {
Py {
inner: chanlun::business::bsp::::(
,
,
,
Rc::clone(&.borrow().inner),
Rc::clone(&K.borrow().inner),
),
}
}
}
// ========== 观察者 ==========
#[pyclass(name = "观察者", unsendable)]
pub struct Py {
pub(crate) inner: chanlun::business::observer::,
}
#[pymethods]
impl Py {
#[new]
#[pyo3(signature = (符号, 周期, 配置 = None))]
fn new(
: String,
: i64,
: Option<&Bound<'_, Py>>,
py: Python<'_>,
) -> PyResult<Self> {
let config = match {
Some(cfg) => cfg.borrow().to_rust_config(py)?,
None => chanlun::config::::default(),
};
Ok(Self {
inner: chanlun::business::observer::::new(, , config),
})
}
#[getter]
fn (&self) -> String {
self.inner.()
}
#[getter]
fn K线(&self) -> Option<K线Py> {
self.inner.K线().map(|k| K线Py {
inner: (**k).clone(),
})
}
#[getter]
fn K(&self) -> Option<K线Py> {
self.inner.K().map(|k| K线Py {
inner: Rc::clone(k),
})
}
#[getter]
fn (&self) -> String {
self.inner..clone()
}
#[getter]
fn (&self) -> i64 {
self.inner.
}
fn (&mut self) {
self.inner.();
}
fn K线(&mut self, K: &Bound<'_, K线Py>) {
self.inner.K线(K.borrow().inner.clone());
}
fn (&mut self) {
self.inner.();
}
fn _保存数据(&self, root: Option<&str>) {
self.inner._保存数据(root);
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
: &str,
: Option<&Bound<'_, Py>>,
py: Python<'_>,
) -> PyResult<Self> {
let config = match {
Some(cfg) => Some(cfg.borrow().to_rust_config(py)?),
None => None,
};
chanlun::business::observer::::(, config)
.map(|inner| Self { inner })
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e))
}
// ---- 序列 getters ----
#[getter]
fn K线序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for k in &self.inner.K线序列 {
list.append(K线Py {
inner: (**k).clone(),
})?;
}
Ok(list.into())
}
#[getter]
fn K线序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for k in &self.inner.K线序列 {
list.append(K线Py {
inner: Rc::clone(k),
})?;
}
Ok(list.into())
}
#[getter]
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for f in &self.inner. {
list.append(Py {
inner: Rc::clone(f),
})?;
}
Ok(list.into())
}
#[getter]
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for d in &self.inner. {
list.append(线Py {
inner: Rc::clone(d),
})?;
}
Ok(list.into())
}
#[getter]
fn _中枢序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for h in &self.inner._中枢序列 {
list.append(Py {
inner: Rc::clone(h),
})?;
}
Ok(list.into())
}
#[getter]
fn 线(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for d in &self.inner.线 {
list.append(线Py {
inner: Rc::clone(d),
})?;
}
Ok(list.into())
}
#[getter]
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for h in &self.inner. {
list.append(Py {
inner: Rc::clone(h),
})?;
}
Ok(list.into())
}
#[getter]
fn 线(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for d in &self.inner.线 {
list.append(线Py {
inner: Rc::clone(d),
})?;
}
Ok(list.into())
}
#[getter]
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for h in &self.inner. {
list.append(Py {
inner: Rc::clone(h),
})?;
}
Ok(list.into())
}
}
// ========== K线合成器 ==========
#[pyclass(name = "K线合成器", unsendable)]
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(, ),
}
}
fn K线(
&mut self,
K: &Bound<'_, K线Py>,
py: Python<'_>,
) -> PyResult<Vec<(i64, K线Py)>> {
let results = self.inner.K线(K.borrow().inner.clone());
Ok(results
.into_iter()
.map(|(, k)| (, K线Py { inner: k }))
.collect())
}
fn K线(&self, : i64) -> Option<K线Py> {
self.inner
.K线()
.map(|k| K线Py { inner: k.clone() })
}
#[getter]
fn (&self) -> String {
self.inner..clone()
}
#[getter]
fn (&self) -> Vec<i64> {
self.inner..clone()
}
}
// ========== 立体分析器 ==========
#[pyclass(name = "立体分析器", unsendable)]
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.downcast::<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,
),
})
}
fn K线(&mut self, K: &Bound<'_, K线Py>) {
self.inner.K线(K.borrow().inner.clone());
}
// 获取观察者 deferred — 观察者 doesn't implement Clone, needs core change
fn _保存数据(&self) {
self.inner._保存数据();
}
#[getter]
fn (&self) -> Vec<i64> {
self.inner..clone()
}
}
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(())
}
+222
View File
@@ -0,0 +1,222 @@
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyType};
use std::collections::HashMap;
/// 缠论配置 — Python binding
#[pyclass(name = "缠论配置")]
pub struct Py {
fields: HashMap<String, Py<PyAny>>,
}
#[pymethods]
impl Py {
#[new]
#[pyo3(signature = (**kwargs))]
fn new(kwargs: Option<&Bound<'_, PyDict>>) -> PyResult<Self> {
let default_config = chanlun::config::::default();
let mut fields = config_to_field_dict(&default_config)?;
if let Some(kwargs) = kwargs {
for (key, value) in kwargs.iter() {
let key: String = key.extract()?;
if fields.contains_key(&key) {
fields.insert(key, value.clone().unbind());
} else {
return Err(pyo3::exceptions::PyAttributeError::new_err(format!(
"缠论配置 没有字段: {key}"
)));
}
}
}
Ok(Self { fields })
}
fn __getattr__(&self, name: &str, py: Python<'_>) -> PyResult<Py<PyAny>> {
match self.fields.get(name) {
Some(v) => Ok(v.clone_ref(py)),
None => Err(pyo3::exceptions::PyAttributeError::new_err(format!(
"缠论配置 没有字段: {name}"
))),
}
}
fn __setattr__(&mut self, name: &str, value: &Bound<'_, PyAny>) -> PyResult<()> {
if self.fields.contains_key(name) {
self.fields.insert(name.to_string(), value.clone().unbind());
Ok(())
} else {
Err(pyo3::exceptions::PyAttributeError::new_err(format!(
"缠论配置 没有字段: {name}"
)))
}
}
fn __str__(&self) -> String {
format!("缠论配置({} fields)", self.fields.len())
}
fn __repr__(&self) -> String {
self.__str__()
}
fn to_dict(&self, py: Python<'_>) -> PyResult<Py<PyDict>> {
let dict = PyDict::new(py);
for (k, v) in &self.fields {
dict.set_item(k, v.clone_ref(py))?;
}
Ok(dict.into())
}
fn to_json(&self, py: Python<'_>) -> PyResult<String> {
let dict = self.to_dict(py)?;
let json_mod = py.import("json")?;
let dumps = json_mod.getattr("dumps")?;
dumps.call1((dict,))?.extract()
}
fn (&self, py: Python<'_>, path: Option<&str>) -> PyResult<()> {
let path = path.unwrap_or("缠论配置.json");
let json = self.to_json(py)?;
std::fs::write(path, json).map_err(|e| pyo3::exceptions::PyIOError::new_err(e.to_string()))
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
py: Python<'_>,
path: Option<&str>,
) -> PyResult<Self> {
let path = path.unwrap_or("缠论配置.json");
let json_str = std::fs::read_to_string(path)
.map_err(|e| pyo3::exceptions::PyIOError::new_err(e.to_string()))?;
Self::from_json_str(py, &json_str)
}
#[classmethod]
fn from_dict(_cls: &Bound<'_, PyType>, data: &Bound<'_, PyDict>) -> PyResult<Self> {
let default_config = chanlun::config::::default();
let mut fields = config_to_field_dict(&default_config)?;
for (key, value) in data.iter() {
let key: String = key.extract()?;
fields.insert(key, value.clone().unbind());
}
dict_to_rust_config(&fields)?;
Ok(Self { fields })
}
#[classmethod]
fn from_json(_cls: &Bound<'_, PyType>, py: Python<'_>, json_str: &str) -> PyResult<Self> {
Self::from_json_str(py, json_str)
}
#[classmethod]
fn (_cls: &Bound<'_, PyType>) -> PyResult<Self> {
let config = chanlun::config::::default().();
let fields = config_to_field_dict(&config)?;
Ok(Self { fields })
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, PyAny>,
: &Bound<'_, PyDict>,
) -> PyResult<Py<PyDict>> {
let py = .py();
let result = PyDict::new(py);
if let Ok(default_dict) = .downcast::<PyDict>() {
for (key, value) in default_dict.iter() {
if .contains(&key)? {
result.set_item(key.clone(), .get_item(&key)?)?;
} else {
result.set_item(key, value)?;
}
}
}
Ok(result.into())
}
fn (
&self,
py: Python<'_>,
other: &Bound<'_, Py>,
) -> PyResult<HashMap<String, (Py<PyAny>, Py<PyAny>)>> {
let other_ref = other.borrow();
let mut diff = HashMap::new();
for (key, val) in &self.fields {
if let Some(other_val) = other_ref.fields.get(key) {
let a = val.clone_ref(py);
let b = other_val.clone_ref(py);
let eq = a.bind(py).eq(b.bind(py))?;
if !eq {
diff.insert(key.clone(), (val.clone_ref(py), other_val.clone_ref(py)));
}
}
}
Ok(diff)
}
}
impl Py {
fn from_json_str(py: Python<'_>, json_str: &str) -> PyResult<Self> {
let json_mod = py.import("json")?;
let loads = json_mod.getattr("loads")?;
let data: Bound<'_, PyDict> = loads.call1((json_str,))?.extract()?;
let mut fields: HashMap<String, Py<PyAny>> = HashMap::new();
for (key, value) in data.iter() {
let key: String = key.extract()?;
fields.insert(key, value.clone().unbind());
}
dict_to_rust_config(&fields)?;
Ok(Self { fields })
}
pub(crate) fn to_rust_config(&self, py: Python<'_>) -> PyResult<chanlun::config::> {
dict_to_rust_config(&self.fields)
}
}
fn config_to_field_dict(
config: &chanlun::config::,
) -> PyResult<HashMap<String, Py<PyAny>>> {
Python::attach(|py| {
let json_str = serde_json::to_string(config)
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))?;
let json_mod = py.import("json")?;
let loads = json_mod.getattr("loads")?;
let data: Bound<'_, PyDict> = loads.call1((json_str,))?.extract()?;
let mut fields = HashMap::new();
for (key, value) in data.iter() {
let key: String = key.extract()?;
fields.insert(key, value.clone().unbind());
}
Ok(fields)
})
}
fn dict_to_rust_config(
fields: &HashMap<String, Py<PyAny>>,
) -> PyResult<chanlun::config::> {
Python::attach(|py| {
let json_mod = py.import("json")?;
let dict = PyDict::new(py);
for (k, v) in fields {
dict.set_item(k, v.clone_ref(py))?;
}
let dumps = json_mod.getattr("dumps")?;
let json_str: String = dumps.call1((dict,))?.extract()?;
serde_json::from_str(&json_str)
.map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("配置转换失败: {e}")))
})
}
pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Py>()?;
Ok(())
}
+576
View File
@@ -0,0 +1,576 @@
use pyo3::prelude::*;
use pyo3::types::PyType;
// ========== 平滑异同移动平均线 ==========
#[pyclass(name = "平滑异同移动平均线")]
#[derive(Clone)]
pub struct 线Py {
pub(crate) inner: chanlun::indicators::线,
}
#[pymethods]
impl 线Py {
#[new]
fn new() -> Self {
unimplemented!("使用 首次计算 或 增量计算 创建")
}
#[getter]
fn (&self) -> String {
self.inner..to_string()
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[getter]
fn 线(&self) -> i64 {
self.inner.线
}
#[getter]
fn 线(&self) -> i64 {
self.inner.线
}
#[getter]
fn (&self) -> i64 {
self.inner.
}
#[getter]
fn DIF(&self) -> Option<f64> {
self.inner.DIF
}
#[getter]
fn DEA(&self) -> Option<f64> {
self.inner.DEA
}
#[getter]
fn MACD柱(&self) -> f64 {
self.inner.MACD柱
}
#[getter]
fn 线EMA(&self) -> Option<f64> {
self.inner.线EMA
}
#[getter]
fn 线EMA(&self) -> Option<f64> {
self.inner.线EMA
}
#[getter]
fn DEA_EMA(&self) -> Option<f64> {
self.inner.DEA_EMA
}
fn __str__(&self) -> String {
format!(
"平滑异同移动平均线(DIF={:?}, DEA={:?}, BAR={:?})",
self.inner.DIF, self.inner.DEA, self.inner.MACD柱
)
}
fn __repr__(&self) -> String {
self.__str__()
}
#[classmethod]
#[pyo3(signature = (初始收盘价, 初始时间, 快线周期 = None, 慢线周期 = None, 信号周期 = None))]
fn (
_cls: &Bound<'_, PyType>,
: f64,
: i64,
线: Option<i64>,
线: Option<i64>,
: Option<i64>,
) -> Self {
let inner = chanlun::indicators::线::(
,
,
线.unwrap_or(12),
线.unwrap_or(26),
.unwrap_or(9),
);
Self { inner }
}
#[classmethod]
#[pyo3(signature = (k线, 计算方式, 快线周期 = None, 慢线周期 = None, 信号周期 = None))]
fn _K线(
_cls: &Bound<'_, PyType>,
k线: &Bound<'_, PyAny>,
: &str,
线: Option<i64>,
线: Option<i64>,
: Option<i64>,
) -> PyResult<Self> {
let = crate::indicators_py::K线取值(k线, )?;
Ok(Self {
inner: chanlun::indicators::线::(
,
(k线)?,
线.unwrap_or(12),
线.unwrap_or(26),
.unwrap_or(9),
),
})
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
MACD: &Bound<'_, 线Py>,
: f64,
: i64,
) -> Self {
let inner = chanlun::indicators::线::(
&MACD.borrow().inner,
,
,
);
Self { inner }
}
#[classmethod]
fn _K线(
_cls: &Bound<'_, PyType>,
MACD: &Bound<'_, 线Py>,
K线: &Bound<'_, PyAny>,
: &str,
) -> PyResult<Self> {
let = K线取值(K线, )?;
Ok(Self {
inner: chanlun::indicators::线::(
&MACD.borrow().inner,
,
(K线)?,
),
})
}
}
// ========== 相对强弱指数 ==========
#[pyclass(name = "相对强弱指数")]
#[derive(Clone)]
pub struct Py {
pub(crate) inner: chanlun::indicators::,
}
#[pymethods]
impl Py {
#[new]
fn new() -> Self {
unimplemented!("使用 首次计算 或 增量计算 创建")
}
#[getter]
fn (&self) -> String {
self.inner..to_string()
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[getter]
fn (&self) -> i64 {
self.inner.
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[getter]
fn RSI_SMA周期(&self) -> Option<i64> {
self.inner.RSI_SMA周期
}
#[getter]
fn RSI(&self) -> Option<f64> {
self.inner.RSI
}
#[getter]
fn (&self) -> Option<f64> {
self.inner.
}
#[getter]
fn (&self) -> Option<f64> {
self.inner.
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[getter]
fn RSI_SMA(&self) -> Option<f64> {
self.inner.RSI_SMA
}
#[getter]
fn RSI历史队列(&self) -> Vec<f64> {
self.inner.RSI历史队列.clone()
}
fn __str__(&self) -> String {
format!("相对强弱指数(RSI={:?})", self.inner.RSI)
}
fn __repr__(&self) -> String {
self.__str__()
}
#[classmethod]
#[pyo3(signature = (初始收盘价, 初始时间, 周期 = None, 超买阈值 = None, 超卖阈值 = None, RSI_SMA周期 = None))]
fn (
_cls: &Bound<'_, PyType>,
: f64,
: i64,
: Option<i64>,
: Option<f64>,
: Option<f64>,
RSI_SMA周期: Option<i64>,
) -> Self {
Self {
inner: chanlun::indicators::::(
,
,
.unwrap_or(14),
.unwrap_or(70.0),
.unwrap_or(30.0),
RSI_SMA周期,
),
}
}
#[classmethod]
#[pyo3(signature = (k线, 计算方式, 周期 = None, 超买阈值 = None, 超卖阈值 = None, RSI_SMA周期 = None))]
fn _K线(
_cls: &Bound<'_, PyType>,
k线: &Bound<'_, PyAny>,
: &str,
: Option<i64>,
: Option<f64>,
: Option<f64>,
RSI_SMA周期: Option<i64>,
) -> PyResult<Self> {
let = K线取值(k线, )?;
Ok(Self {
inner: chanlun::indicators::::(
,
(k线)?,
.unwrap_or(14),
.unwrap_or(70.0),
.unwrap_or(30.0),
RSI_SMA周期,
),
})
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
RSI: &Bound<'_, Py>,
: f64,
: i64,
) -> Self {
Self {
inner: chanlun::indicators::::(
&RSI.borrow().inner,
,
,
),
}
}
#[classmethod]
fn _K线(
_cls: &Bound<'_, PyType>,
RSI: &Bound<'_, Py>,
K线: &Bound<'_, PyAny>,
: &str,
) -> PyResult<Self> {
let = K线取值(K线, )?;
Ok(Self {
inner: chanlun::indicators::::(
&RSI.borrow().inner,
,
(K线)?,
),
})
}
}
// ========== 随机指标 ==========
#[pyclass(name = "随机指标")]
#[derive(Clone)]
pub struct Py {
pub(crate) inner: chanlun::indicators::,
}
#[pymethods]
impl Py {
#[new]
fn new() -> Self {
unimplemented!("使用 首次计算 或 增量计算 创建")
}
#[getter]
fn (&self) -> String {
self.inner..to_string()
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[getter]
fn N(&self) -> i64 {
self.inner.N
}
#[getter]
fn M1(&self) -> i64 {
self.inner.M1
}
#[getter]
fn M2(&self) -> i64 {
self.inner.M2
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[getter]
fn RSV(&self) -> Option<f64> {
self.inner.RSV
}
#[getter]
fn K(&self) -> Option<f64> {
self.inner.K
}
#[getter]
fn D(&self) -> Option<f64> {
self.inner.D
}
#[getter]
fn J(&self) -> Option<f64> {
self.inner.J
}
#[getter]
fn (&self) -> Vec<f64> {
self.inner..clone()
}
#[getter]
fn (&self) -> Vec<f64> {
self.inner..clone()
}
#[getter]
fn RSV(&self) -> Option<f64> {
self.inner.RSV
}
#[getter]
fn K(&self) -> Option<f64> {
self.inner.K
}
#[getter]
fn D(&self) -> Option<f64> {
self.inner.D
}
fn __str__(&self) -> String {
format!(
"随机指标(K={:?}, D={:?}, J={:?})",
self.inner.K, self.inner.D, self.inner.J
)
}
fn __repr__(&self) -> String {
self.__str__()
}
#[classmethod]
#[pyo3(signature = (初始最高价, 初始最低价, 初始收盘价, 初始时间, N = None, M1 = None, M2 = None, 超买阈值 = None, 超卖阈值 = None))]
fn (
_cls: &Bound<'_, PyType>,
: f64,
: f64,
: f64,
: i64,
N: Option<i64>,
M1: Option<i64>,
M2: Option<i64>,
: Option<f64>,
: Option<f64>,
) -> Self {
Self {
inner: chanlun::indicators::::(
,
,
,
,
N.unwrap_or(9),
M1.unwrap_or(3),
M2.unwrap_or(3),
.unwrap_or(80.0),
.unwrap_or(20.0),
),
}
}
#[classmethod]
#[pyo3(signature = (k线, _计算方式, RSV周期 = None, K值平滑周期 = None, D值平滑周期 = None, 超买阈值 = None, 超卖阈值 = None))]
fn _K线(
_cls: &Bound<'_, PyType>,
k线: &Bound<'_, PyAny>,
_计算方式: &str,
RSV周期: Option<i64>,
K值平滑周期: Option<i64>,
D值平滑周期: Option<i64>,
: Option<f64>,
: Option<f64>,
) -> PyResult<Self> {
let : f64 = k线.getattr("收盘价")?.extract()?;
let : f64 = k线.getattr("")?.extract()?;
let : f64 = k线.getattr("")?.extract()?;
Ok(Self {
inner: chanlun::indicators::::(
,
,
,
(k线)?,
RSV周期.unwrap_or(9),
K值平滑周期.unwrap_or(3),
D值平滑周期.unwrap_or(3),
.unwrap_or(80.0),
.unwrap_or(20.0),
),
})
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
KDJ: &Bound<'_, Py>,
: f64,
: f64,
: f64,
: i64,
) -> Self {
Self {
inner: chanlun::indicators::::(
&KDJ.borrow().inner,
,
,
,
,
),
}
}
#[classmethod]
fn _K线(
_cls: &Bound<'_, PyType>,
KDJ: &Bound<'_, Py>,
K线: &Bound<'_, PyAny>,
_计算方式: &str,
) -> PyResult<Self> {
let : f64 = K线.getattr("收盘价")?.extract()?;
let : f64 = K线.getattr("")?.extract()?;
let : f64 = K线.getattr("")?.extract()?;
Ok(Self {
inner: chanlun::indicators::::(
&KDJ.borrow().inner,
,
,
,
(K线)?,
),
})
}
}
// ========== 指标 (static namespace) ==========
#[pyclass(name = "指标")]
pub struct Py;
#[pymethods]
impl Py {
#[classmethod]
fn K线取值(
_cls: &Bound<'_, PyType>,
k线: &Bound<'_, PyAny>,
: &str,
) -> PyResult<f64> {
K线取值(k线, )
}
}
// ========== Helper functions ==========
pub(crate) fn K线取值(k线: &Bound<'_, PyAny>, : &str) -> PyResult<f64> {
let : f64 = k线.getattr("开盘价")?.extract()?;
let : f64 = k线.getattr("")?.extract()?;
let : f64 = k线.getattr("")?.extract()?;
let : f64 = k线.getattr("收盘价")?.extract()?;
Ok(chanlun::indicators::K线取值(
,
,
,
,
,
))
}
fn (k线: &Bound<'_, PyAny>) -> PyResult<i64> {
let ts = k线.getattr("时间戳")?;
if let Ok(i) = ts.extract::<i64>() {
return Ok(i);
}
// Try float
if let Ok(f) = ts.extract::<f64>() {
return Ok(f as i64);
}
// Try calling .timestamp() if it's a datetime
if let Ok(method) = ts.getattr("timestamp") {
let result: f64 = method.call0()?.extract()?;
return Ok(result as i64);
}
Err(pyo3::exceptions::PyTypeError::new_err("无法获取时间戳"))
}
pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<线Py>()?;
m.add_class::<Py>()?;
m.add_class::<Py>()?;
m.add_class::<Py>()?;
Ok(())
}
+498
View File
@@ -0,0 +1,498 @@
use pyo3::prelude::*;
use pyo3::types::{PyBytes, PyType};
use std::collections::HashMap;
use crate::config_py::Py;
use crate::indicators_py::{线Py, Py, Py};
use crate::types_py::Py;
// ========== K线 ==========
#[pyclass(name = "K线")]
#[derive(Clone)]
pub struct K线Py {
pub(crate) inner: chanlun::kline::bar::K线,
}
#[pymethods]
impl K线Py {
#[new]
#[pyo3(signature = (标识 = "bar", 序号 = 0, 周期 = 60, 时间戳 = 0, 高 = 0.0, 低 = 0.0, 开盘价 = 0.0, 收盘价 = 0.0, 成交量 = 0.0))]
fn new(
: &str,
: i64,
: i64,
: i64,
: f64,
: f64,
: f64,
: f64,
: f64,
) -> Self {
Self {
inner: chanlun::kline::bar::K线 {
: .to_string(),
,
,
,
,
,
,
,
,
macd: None,
rsi: None,
kdj: None,
},
}
}
#[getter]
fn (&self) -> String {
self.inner..clone()
}
#[setter]
fn set_标识(&mut self, v: String) {
self.inner. = v;
}
#[getter]
fn (&self) -> i64 {
self.inner.
}
#[setter]
fn set_序号(&mut self, v: i64) {
self.inner. = v;
}
#[getter]
fn (&self) -> i64 {
self.inner.
}
#[setter]
fn set_周期(&mut self, v: i64) {
self.inner. = v;
}
#[getter]
fn (&self) -> i64 {
self.inner.
}
#[setter]
fn set_时间戳(&mut self, v: i64) {
self.inner. = v;
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[setter]
fn set_高(&mut self, v: f64) {
self.inner. = v;
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[setter]
fn set_低(&mut self, v: f64) {
self.inner. = v;
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[setter]
fn set_开盘价(&mut self, v: f64) {
self.inner. = v;
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[setter]
fn set_收盘价(&mut self, v: f64) {
self.inner. = v;
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[setter]
fn set_成交量(&mut self, v: f64) {
self.inner. = v;
}
#[getter]
fn (&self) -> Py {
Py {
inner: self.inner.(),
}
}
#[getter]
fn MACD(&self) -> Option<线Py> {
self.inner
.macd
.as_ref()
.map(|m| 线Py { inner: m.clone() })
}
#[getter]
fn RSI(&self) -> Option<Py> {
self.inner
.rsi
.as_ref()
.map(|r| Py { inner: r.clone() })
}
#[getter]
fn KDJ(&self) -> Option<Py> {
self.inner
.kdj
.as_ref()
.map(|k| Py { inner: k.clone() })
}
fn __str__(&self) -> String {
format!("{}", self.inner)
}
fn __repr__(&self) -> String {
self.__str__()
}
fn __bytes__(&self, py: Python<'_>) -> Py<PyBytes> {
PyBytes::new(py, &self.inner.to_bytes()).into()
}
#[classmethod]
#[pyo3(signature = (标识, 时间戳, 开盘价, 最高价, 最低价, 收盘价, 成交量, 序号 = None, 周期 = None))]
fn K(
_cls: &Bound<'_, PyType>,
: &str,
: i64,
: f64,
: f64,
: f64,
: f64,
: f64,
: Option<i64>,
: Option<i64>,
) -> Self {
Self {
inner: chanlun::kline::bar::K线::K(
,
,
,
,
,
,
,
.unwrap_or(0),
.unwrap_or(60),
),
}
}
#[classmethod]
fn DAT文件(
_cls: &Bound<'_, PyType>,
: &str,
K线序列: Vec<Py<Self>>,
py: Python<'_>,
) -> PyResult<()> {
let refs: Vec<_> = K线序列.iter().map(|k| k.bind(py).borrow()).collect();
let bars: Vec<&chanlun::kline::bar::K线> = refs.iter().map(|r| &r.inner).collect();
chanlun::kline::bar::K线::DAT文件(, &bars)
.map_err(|e| pyo3::exceptions::PyIOError::new_err(e.to_string()))
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, PyBytes>,
: i64,
: &str,
) -> Option<Self> {
chanlun::kline::bar::K线::(.as_bytes(), , )
.map(|inner| Self { inner })
}
#[classmethod]
fn MACD(
_cls: &Bound<'_, PyType>,
k线序列: Vec<Py<Self>>,
: &Bound<'_, Self>,
: &Bound<'_, Self>,
py: Python<'_>,
) -> HashMap<String, f64> {
let refs: Vec<_> = k线序列.iter().map(|k| k.bind(py).borrow()).collect();
let bars: Vec<&chanlun::kline::bar::K线> = refs.iter().map(|r| &r.inner).collect();
chanlun::kline::bar::K线::MACD(&bars, &.borrow().inner, &.borrow().inner)
}
#[staticmethod]
fn (
: Vec<Py<Self>>,
: &Bound<'_, Self>,
: &Bound<'_, Self>,
) -> Option<Vec<Py<Self>>> {
let start_ptr = .as_ptr();
let end_ptr = .as_ptr();
let start_idx = .iter().position(|k| k.as_ptr() == start_ptr)?;
let end_idx = .iter().position(|k| k.as_ptr() == end_ptr)?;
if start_idx <= end_idx {
Some(
.into_iter()
.skip(start_idx)
.take(end_idx - start_idx + 1)
.collect(),
)
} else {
None
}
}
}
// ========== 缠论K线 ==========
#[pyclass(name = "缠论K线", unsendable)]
#[derive(Clone)]
pub struct K线Py {
pub(crate) inner: std::rc::Rc<chanlun::kline::chan_kline::K线>,
}
#[pymethods]
impl K线Py {
#[new]
fn new() -> Self {
unimplemented!("使用 创建缠K 创建")
}
#[getter]
fn (&self) -> i64 {
self.inner.
}
#[getter]
fn (&self) -> i64 {
self.inner.
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[getter]
fn (&self) -> Py {
Py {
inner: 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) -> String {
self.inner..clone()
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
#[getter]
fn (&self) -> i64 {
self.inner.
}
#[getter]
fn (&self) -> i64 {
self.inner.
}
#[getter]
fn K线(&self) -> K线Py {
K线Py {
inner: (*self.inner.K线).clone(),
}
}
fn __str__(&self) -> String {
format!("{}", self.inner)
}
fn __repr__(&self) -> String {
self.__str__()
}
fn (&self) -> Self {
Self {
inner: std::rc::Rc::new(self.inner.()),
}
}
#[getter]
fn MACD柱子匹配(&self) -> bool {
self.inner.MACD柱子匹配()
}
#[getter]
fn RSI匹配(&self) -> bool {
self.inner.RSI匹配()
}
#[getter]
fn KDJ匹配(&self) -> bool {
self.inner.KDJ匹配()
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
线: Vec<Py<Self>>,
k线: &Bound<'_, Self>,
py: Python<'_>,
) -> i64 {
let rc_list: Vec<_> = 线
.iter()
.map(|k| std::rc::Rc::clone(&k.bind(py).borrow().inner))
.collect();
chanlun::kline::chan_kline::K线::(&rc_list, &k线.borrow().inner)
}
#[classmethod]
fn K(
_cls: &Bound<'_, PyType>,
: i64,
: f64,
: f64,
: &Bound<'_, Py>,
: Option<&Bound<'_, crate::types_py::Py>>,
: i64,
k: &Bound<'_, K线Py>,
: Option<&Bound<'_, Self>>,
) -> Self {
let prev_ref = .map(|prev| prev.borrow());
let prev_inner = prev_ref.as_ref().map(|r| r.inner.as_ref());
let inner = chanlun::kline::chan_kline::K线::K(
,
,
,
.borrow().inner,
.map(|s| s.borrow().inner),
,
std::rc::Rc::new(k.borrow().inner.clone()),
prev_inner,
);
Self {
inner: std::rc::Rc::new(inner),
}
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
K: Option<&Bound<'_, Self>>,
K: &Bound<'_, Self>,
K: &Bound<'_, K线Py>,
: &Bound<'_, Py>,
py: Python<'_>,
) -> PyResult<(Option<Self>, Option<String>)> {
let mut ck_inner = (*K.borrow().inner).clone();
let config = .borrow().to_rust_config(py)?;
let prev_ref = K.map(|prev| prev.borrow());
let prev_inner = prev_ref.as_ref().map(|r| r.inner.as_ref());
let (result, mode) = chanlun::kline::chan_kline::K线::(
prev_inner,
&mut ck_inner,
&K.borrow().inner,
&config,
);
Ok((result.map(|rc| Self { inner: rc }), mode))
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
K线: &Bound<'_, K线Py>,
K序列: Vec<Py<Self>>,
K序列: Vec<Py<K线Py>>,
: &Bound<'_, Py>,
py: Python<'_>,
) -> PyResult<(String, Option<Py<PyAny>>)> {
let ck_inner = K线.borrow().inner.clone();
let config = .borrow().to_rust_config(py)?;
let mut ck_seq: Vec<_> = K序列
.iter()
.map(|k| std::rc::Rc::clone(&k.bind(py).borrow().inner))
.collect();
let mut bar_seq: Vec<_> = K序列
.iter()
.map(|k| std::rc::Rc::new(k.bind(py).borrow().inner.clone()))
.collect();
let (status, _fractal) = chanlun::kline::chan_kline::K线::(
ck_inner,
&mut ck_seq,
&mut bar_seq,
&config,
);
Ok((status, None))
}
#[staticmethod]
fn (
: Vec<Py<Self>>,
: &Bound<'_, Self>,
: &Bound<'_, Self>,
) -> Option<Vec<Py<Self>>> {
let start_ptr = .as_ptr();
let end_ptr = .as_ptr();
let start_idx = .iter().position(|k| k.as_ptr() == start_ptr)?;
let end_idx = .iter().position(|k| k.as_ptr() == end_ptr)?;
if start_idx <= end_idx {
Some(
.into_iter()
.skip(start_idx)
.take(end_idx - start_idx + 1)
.collect(),
)
} else {
None
}
}
}
pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<K线Py>()?;
m.add_class::<K线Py>()?;
Ok(())
}
+29
View File
@@ -0,0 +1,29 @@
use pyo3::prelude::*;
mod algorithm_py;
mod business_py;
mod config_py;
mod indicators_py;
mod kline_py;
mod structure_py;
mod types_py;
/// 缠论技术分析库 — Rust 高性能实现
#[pymodule]
fn _chanlun(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
// 阶段 1: 枚举和基础类型
types_py::register(m)?;
// 阶段 2: 配置
config_py::register(m)?;
// 阶段 3: 技术指标
indicators_py::register(m)?;
// 阶段 4: K线
kline_py::register(m)?;
// 阶段 5: 结构
structure_py::register(m)?;
// 阶段 6: 算法
algorithm_py::register(m)?;
// 阶段 7: 业务
business_py::register(m)?;
Ok(())
}
+825
View File
@@ -0,0 +1,825 @@
use pyo3::prelude::*;
use pyo3::types::PyType;
use std::collections::HashMap;
use std::rc::Rc;
use crate::config_py::Py;
use crate::kline_py::{K线Py, K线Py};
use crate::types_py::{Py, Py, Py};
// ========== 分型 ==========
#[pyclass(name = "分型", unsendable)]
#[derive(Clone)]
pub struct Py {
pub(crate) inner: Rc<chanlun::structure::fractal_obj::>,
}
#[pymethods]
impl Py {
#[new]
fn new(
: Option<&Bound<'_, K线Py>>,
: &Bound<'_, K线Py>,
: Option<&Bound<'_, K线Py>>,
) -> Self {
Self {
inner: Rc::new(chanlun::structure::fractal_obj::::new(
.map(|k| Rc::clone(&k.borrow().inner)),
Rc::clone(&.borrow().inner),
.map(|k| Rc::clone(&k.borrow().inner)),
)),
}
}
#[getter]
fn (&self) -> Option<K线Py> {
self.inner..as_ref().map(|k| K线Py {
inner: Rc::clone(k),
})
}
#[getter]
fn (&self) -> K线Py {
K线Py {
inner: Rc::clone(&self.inner.),
}
}
#[getter]
fn (&self) -> Option<K线Py> {
self.inner..as_ref().map(|k| K线Py {
inner: Rc::clone(k),
})
}
#[getter]
fn (&self) -> Py {
Py {
inner: self.inner.,
}
}
#[getter]
fn (&self) -> i64 {
self.inner.
}
#[getter]
fn (&self) -> f64 {
self.inner.
}
fn __str__(&self) -> String {
format!("{}", self.inner)
}
fn __repr__(&self) -> String {
self.__str__()
}
#[getter]
fn (&self) -> Option<(Py, Py, Py)> {
self.inner.().map(|(a, b, c)| {
(
Py { inner: a },
Py { inner: b },
Py { inner: c },
)
})
}
#[getter]
fn (&self) -> String {
self.inner.().to_string()
}
#[getter]
fn MACD柱子分型匹配(&self) -> bool {
self.inner.MACD柱子分型匹配()
}
#[classmethod]
#[pyo3(signature = (左, 右, 模式 = ""))]
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, Self>,
: &Bound<'_, Self>,
: &str,
) -> bool {
chanlun::structure::fractal_obj::::(
&.borrow().inner,
&.borrow().inner,
,
)
}
#[staticmethod]
fn K序列中获取分型(
K线序列: Vec<Py<K线Py>>,
: &Bound<'_, K线Py>,
py: Python<'_>,
) -> Option<Self> {
let ck_seq: Vec<Rc<chanlun::kline::chan_kline::K线>> = K线序列
.iter()
.map(|k| Rc::clone(&k.bind(py).borrow().inner))
.collect();
chanlun::structure::fractal_obj::::K序列中获取分型(
&ck_seq,
&.borrow().inner,
)
.map(|inner| Self {
inner: Rc::new(inner),
})
}
#[staticmethod]
fn (
: &Bound<'_, PyAny>, : &Bound<'_, Self>
) -> PyResult<()> {
let py = .py();
let inner = Rc::clone(&.borrow().inner);
let wrapper = Py::new(
py,
Self {
inner: Rc::clone(&inner),
},
)?;
.call_method1("append", (wrapper,))?;
Ok(())
}
}
// ========== 虚线 ==========
#[pyclass(name = "虚线", unsendable)]
#[derive(Clone)]
pub struct 线Py {
pub(crate) inner: Rc<chanlun::structure::dash_line::线>,
}
#[pymethods]
impl 线Py {
#[new]
#[pyo3(signature = (序号, 标识, 文, 武, 级别, 有效性 = true))]
fn new(
: i64,
: String,
: &Bound<'_, Py>,
: &Bound<'_, Py>,
: i64,
: bool,
) -> Self {
Self {
inner: Rc::new(chanlun::structure::dash_line::线::new(
,
,
Rc::clone(&.borrow().inner),
Rc::clone(&.borrow().inner),
,
,
)),
}
}
// ---- 基础 getters ----
#[getter]
fn (&self) -> String {
self.inner..clone()
}
#[getter]
fn (&self) -> i64 {
self.inner.
}
#[getter]
fn (&self) -> i64 {
self.inner.
}
#[getter]
fn (&self) -> Py {
Py {
inner: Rc::clone(&self.inner.),
}
}
#[getter]
fn (&self) -> Py {
Py {
inner: Rc::clone(&self.inner.),
}
}
#[getter]
fn (&self) -> bool {
self.inner.
}
#[getter]
fn (&self) -> String {
self.inner..clone()
}
#[getter]
fn _特征序列_显示(&self) -> bool {
self.inner._特征序列_显示
}
#[getter]
fn (&self) -> bool {
self.inner.
}
#[getter]
fn K线(&self) -> Option<K线Py> {
self.inner.K线.as_ref().map(|k| K线Py {
inner: Rc::clone(k),
})
}
#[getter]
fn (&self) -> Option<Py> {
self.inner..map(|q| Py { inner: q })
}
#[getter]
fn (&self) -> Option<Self> {
self.inner..as_ref().map(|d| Self {
inner: Rc::clone(d),
})
}
// ---- 序列 getters ----
#[getter]
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for d in &self.inner. {
list.append(Py::new(
py,
Self {
inner: Rc::clone(d),
},
)?)?;
}
Ok(list.into())
}
// 实_中枢序列 / 虚_中枢序列 / 合_中枢序列 — deferred to Phase 6 (中枢Py not yet available)
// ---- 计算属性 ----
#[getter]
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for d in self.inner.() {
list.append(Py::new(
py,
Self {
inner: Rc::clone(d),
},
)?)?;
}
Ok(list.into())
}
#[getter]
fn (&self) -> String {
self.inner.()
}
#[getter]
fn (&self) -> Py {
Py {
inner: self.inner.(),
}
}
#[getter]
fn (&self) -> f64 {
self.inner.()
}
#[getter]
fn (&self) -> f64 {
self.inner.()
}
fn (&self, : &Bound<'_, Self>) -> bool {
self.inner.(&.borrow().inner)
}
fn (&self, : &Bound<'_, Self>) -> bool {
self.inner.(&.borrow().inner)
}
fn K序列(&self, K序列: Vec<Py<K线Py>>, py: Python<'_>) -> PyResult<Py<PyAny>> {
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = K序列
.iter()
.map(|k| Rc::new(k.bind(py).borrow().inner.clone()))
.collect();
let result = self.inner.K序列(&rc_list);
let list = pyo3::types::PyList::empty(py);
for k in &result {
list.append(K线Py {
inner: (**k).clone(),
})?;
}
Ok(list.into())
}
fn K序列(
&self, K序列: Vec<Py<K线Py>>, py: Python<'_>
) -> PyResult<Py<PyAny>> {
let rc_list: Vec<Rc<chanlun::kline::chan_kline::K线>> = K序列
.iter()
.map(|k| Rc::clone(&k.bind(py).borrow().inner))
.collect();
let result = self.inner.K序列(&rc_list);
let list = pyo3::types::PyList::empty(py);
for k in &result {
list.append(K线Py {
inner: Rc::clone(k),
})?;
}
Ok(list.into())
}
#[getter]
fn (&self) -> String {
self.inner.()
}
fn __str__(&self) -> String {
format!("{}", self.inner)
}
fn __repr__(&self) -> String {
self.__str__()
}
// ---- 静态工厂方法 ----
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, Py>,
: &Bound<'_, Py>,
: bool,
) -> Self {
Self {
inner: Rc::new(chanlun::structure::dash_line::线::(
Rc::clone(&.borrow().inner),
Rc::clone(&.borrow().inner),
,
)),
}
}
#[classmethod]
fn 线(_cls: &Bound<'_, PyType>, 线: Vec<Py<Self>>, py: Python<'_>) -> Self {
let rc_list: Vec<Rc<chanlun::structure::dash_line::线>> = 线
.iter()
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
.collect();
Self {
inner: Rc::new(chanlun::structure::dash_line::线::线(
&rc_list,
)),
}
}
// ---- 买卖点模式匹配 ----
#[classmethod]
fn K买卖点模式(
_cls: &Bound<'_, PyType>,
: &str,
K: &Bound<'_, K线Py>,
: &Bound<'_, Py>,
py: Python<'_>,
) -> PyResult<bool> {
let config = .borrow().to_rust_config(py)?;
Ok(chanlun::structure::dash_line::线::K买卖点模式(
,
&K.borrow().inner,
&config,
))
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
K: &Bound<'_, K线Py>,
: &Bound<'_, Py>,
py: Python<'_>,
) -> PyResult<bool> {
let config = .borrow().to_rust_config(py)?;
Ok(chanlun::structure::dash_line::线::(
&K.borrow().inner,
&config,
))
}
#[classmethod]
fn (_cls: &Bound<'_, PyType>, K: &Bound<'_, K线Py>) -> bool {
chanlun::structure::dash_line::线::(&K.borrow().inner)
}
#[classmethod]
fn (_cls: &Bound<'_, PyType>, K: &Bound<'_, K线Py>) -> bool {
chanlun::structure::dash_line::线::(&K.borrow().inner)
}
#[classmethod]
fn (_cls: &Bound<'_, PyType>, K: &Bound<'_, K线Py>) -> bool {
chanlun::structure::dash_line::线::(&K.borrow().inner)
}
// ---- MACD 相关 classmethods ----
#[classmethod]
fn MACD柱子均值(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<K线Py>>,
线: &Bound<'_, Self>,
py: Python<'_>,
) -> f64 {
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = K序列
.iter()
.map(|k| Rc::new(k.bind(py).borrow().inner.clone()))
.collect();
chanlun::structure::dash_line::线::MACD柱子均值(
&rc_list,
&线.borrow().inner,
)
}
#[classmethod]
fn MACD均值(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<K线Py>>,
线: &Bound<'_, Self>,
py: Python<'_>,
) -> bool {
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = K序列
.iter()
.map(|k| Rc::new(k.bind(py).borrow().inner.clone()))
.collect();
chanlun::structure::dash_line::线::MACD均值(
&rc_list,
&线.borrow().inner,
)
}
#[classmethod]
fn MACD均值(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<K线Py>>,
线: &Bound<'_, Self>,
py: Python<'_>,
) -> bool {
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = K序列
.iter()
.map(|k| Rc::new(k.bind(py).borrow().inner.clone()))
.collect();
chanlun::structure::dash_line::线::MACD均值(&rc_list, &线.borrow().inner)
}
#[classmethod]
fn MACD极值(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<K线Py>>,
线: &Bound<'_, Self>,
py: Python<'_>,
) -> bool {
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = K序列
.iter()
.map(|k| Rc::new(k.bind(py).borrow().inner.clone()))
.collect();
chanlun::structure::dash_line::线::MACD极值(&rc_list, &线.borrow().inner)
}
#[classmethod]
fn K线序列MACD趋向背驰(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<K线Py>>,
: &Bound<'_, Py>,
py: Python<'_>,
) -> [bool; 3] {
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = K序列
.iter()
.map(|k| Rc::new(k.bind(py).borrow().inner.clone()))
.collect();
chanlun::structure::dash_line::线::K线序列MACD趋向背驰(
&rc_list,
.borrow().inner,
)
}
#[classmethod]
fn MACD柱子分段(
_cls: &Bound<'_, PyType>,
k线序列: Vec<Py<K线Py>>,
py: Python<'_>,
) -> Vec<Vec<f64>> {
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = k线序列
.iter()
.map(|k| Rc::new(k.bind(py).borrow().inner.clone()))
.collect();
chanlun::structure::dash_line::线::MACD柱子分段(&rc_list)
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
: Vec<i32>,
: usize,
: usize,
) -> Vec<(usize, usize, usize)> {
chanlun::structure::dash_line::线::(
&,
,
,
)
}
#[classmethod]
fn MACD行为(
_cls: &Bound<'_, PyType>,
K序列: Vec<Py<K线Py>>,
: usize,
: usize,
py: Python<'_>,
) -> HashMap<String, String> {
let rc_list: Vec<Rc<chanlun::kline::bar::K线>> = K序列
.iter()
.map(|k| Rc::new(k.bind(py).borrow().inner.clone()))
.collect();
chanlun::structure::dash_line::线::MACD行为(&rc_list, , )
}
// 买卖意义 — deferred to Phase 7 (needs observer)
}
// ========== 线段特征 ==========
#[pyclass(name = "线段特征", unsendable)]
#[derive(Clone)]
pub struct 线Py {
pub(crate) inner: Rc<chanlun::structure::segment_feat::线>,
}
#[pymethods]
impl 线Py {
#[new]
fn new(
: String,
: Vec<Py<线Py>>,
线: &Bound<'_, Py>,
py: Python<'_>,
) -> Self {
let rc_list: Vec<Rc<chanlun::structure::dash_line::线>> =
.iter()
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
.collect();
Self {
inner: Rc::new(chanlun::structure::segment_feat::线::new(
,
rc_list,
线.borrow().inner,
)),
}
}
// ---- getters ----
#[getter]
fn (&self) -> i64 {
self.inner.
}
#[getter]
fn (&self) -> String {
self.inner..clone()
}
#[getter]
fn 线(&self) -> Py {
Py {
inner: self.inner.线,
}
}
#[getter]
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for d in &self.inner. {
list.append(Py::new(
py,
线Py {
inner: Rc::clone(d),
},
)?)?;
}
Ok(list.into())
}
fn __str__(&self) -> String {
format!("{}", self.inner)
}
fn __repr__(&self) -> String {
self.__str__()
}
// ---- instance methods ----
#[getter]
fn (&self) -> String {
self.inner.()
}
#[getter]
fn (&self) -> Py {
Py {
inner: self.inner.(),
}
}
#[getter]
fn (&self) -> Py {
Py {
inner: self.inner.(),
}
}
#[getter]
fn (&self) -> Py {
Py {
inner: self.inner.(),
}
}
#[getter]
fn (&self) -> f64 {
self.inner.()
}
#[getter]
fn (&self) -> f64 {
self.inner.()
}
fn (&mut self, 线: &Bound<'_, 线Py>) -> PyResult<()> {
let inner = Rc::make_mut(&mut self.inner);
inner
.(Rc::clone(&线.borrow().inner))
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e))
}
fn (&mut self, 线: &Bound<'_, 线Py>) -> PyResult<()> {
let inner = Rc::make_mut(&mut self.inner);
inner
.(&Rc::clone(&线.borrow().inner))
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e))
}
// ---- classmethods ----
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
线: Vec<Py<线Py>>,
线: &Bound<'_, Py>,
py: Python<'_>,
) -> Self {
let rc_list: Vec<Rc<chanlun::structure::dash_line::线>> = 线
.iter()
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
.collect();
Self {
inner: Rc::new(chanlun::structure::segment_feat::线::(
rc_list,
线.borrow().inner,
)),
}
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
线: Vec<Py<线Py>>,
线: &Bound<'_, Py>,
: &str,
: bool,
py: Python<'_>,
) -> Vec<Self> {
let rc_list: Vec<Rc<chanlun::structure::dash_line::线>> = 线
.iter()
.map(|d| Rc::clone(&d.bind(py).borrow().inner))
.collect();
chanlun::structure::segment_feat::线::(
&rc_list,
线.borrow().inner,
,
,
)
.into_iter()
.map(|inner| Self { inner })
.collect()
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
: Vec<Py<Self>>,
py: Python<'_>,
) -> Vec<Py> {
let rc_list: Vec<Rc<chanlun::structure::segment_feat::线>> =
.iter()
.map(|s| Rc::clone(&s.bind(py).borrow().inner))
.collect();
chanlun::structure::segment_feat::线::(&rc_list)
.into_iter()
.map(|inner| Py {
inner: Rc::new(inner),
})
.collect()
}
}
// ========== 特征分型 ==========
#[pyclass(name = "特征分型", unsendable)]
#[derive(Clone)]
pub struct Py {
pub(crate) inner: Rc<chanlun::structure::feat_fractal::>,
}
#[pymethods]
impl Py {
#[new]
fn new(
: &Bound<'_, 线Py>,
: &Bound<'_, 线Py>,
: &Bound<'_, 线Py>,
: &Bound<'_, Py>,
) -> Self {
Self {
inner: Rc::new(chanlun::structure::feat_fractal::::new(
Rc::clone(&.borrow().inner),
Rc::clone(&.borrow().inner),
Rc::clone(&.borrow().inner),
.borrow().inner,
)),
}
}
#[getter]
fn (&self) -> 线Py {
线Py {
inner: Rc::clone(&self.inner.),
}
}
#[getter]
fn (&self) -> 线Py {
线Py {
inner: Rc::clone(&self.inner.),
}
}
#[getter]
fn (&self) -> 线Py {
线Py {
inner: Rc::clone(&self.inner.),
}
}
#[getter]
fn (&self) -> Py {
Py {
inner: self.inner.,
}
}
fn __str__(&self) -> String {
format!("{}", self.inner)
}
fn __repr__(&self) -> String {
self.__str__()
}
}
pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Py>()?;
m.add_class::<线Py>()?;
m.add_class::<线Py>()?;
m.add_class::<Py>()?;
Ok(())
}
+408
View File
@@ -0,0 +1,408 @@
use pyo3::prelude::*;
use pyo3::types::PyType;
// ========== 买卖点类型 ==========
#[pyclass(name = "买卖点类型")]
#[derive(Clone)]
pub struct Py {
pub inner: chanlun::types::,
}
#[pymethods]
impl Py {
fn __str__(&self) -> String {
self.inner.to_string()
}
fn __repr__(&self) -> String {
self.inner.to_string()
}
fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
if let Ok(s) = other.extract::<String>() {
return self.inner.to_string() == s;
}
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
return self.inner == other.inner;
}
false
}
fn __hash__(&self) -> u64 {
use std::hash::{Hash, Hasher};
let mut h = std::collections::hash_map::DefaultHasher::new();
self.inner.to_string().hash(&mut h);
h.finish()
}
#[getter]
fn (&self) -> bool {
self.inner.()
}
#[getter]
fn (&self) -> bool {
self.inner.()
}
}
// ========== 相对方向 ==========
#[pyclass(name = "相对方向")]
#[derive(Clone)]
pub struct Py {
pub inner: chanlun::types::,
}
#[pymethods]
impl Py {
fn __str__(&self) -> String {
format!("{}", self.inner)
}
fn __repr__(&self) -> String {
format!("{}", self.inner)
}
fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
return self.inner == other.inner;
}
false
}
fn __hash__(&self) -> u64 {
self.inner as u64
}
fn (&self) -> Self {
Self {
inner: self.inner.(),
}
}
#[getter]
fn (&self) -> bool {
self.inner.()
}
#[getter]
fn (&self) -> bool {
self.inner.()
}
#[getter]
fn (&self) -> bool {
self.inner.()
}
#[getter]
fn (&self) -> bool {
self.inner.()
}
#[getter]
fn (&self) -> bool {
self.inner.()
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>, : f64, : f64, : f64, : f64
) -> Self {
Self {
inner: chanlun::types::::(, , , ),
}
}
}
// ========== 分型结构 ==========
#[pyclass(name = "分型结构")]
#[derive(Clone)]
pub struct Py {
pub inner: chanlun::types::,
}
#[pymethods]
impl Py {
fn __str__(&self) -> String {
self.inner.to_string()
}
fn __repr__(&self) -> String {
self.inner.to_string()
}
fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
return self.inner == other.inner;
}
false
}
fn __hash__(&self) -> u64 {
self.inner as u64
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
: &Bound<'_, PyAny>,
: &Bound<'_, PyAny>,
: &Bound<'_, PyAny>,
: Option<bool>,
: Option<bool>,
) -> PyResult<Option<Self>> {
let = .unwrap_or(false);
let = .unwrap_or(false);
let get_hl = |obj: &Bound<'_, PyAny>| -> PyResult<(f64, f64)> {
Ok((
obj.getattr("")?.extract::<f64>()?,
obj.getattr("")?.extract::<f64>()?,
))
};
let (, ) = get_hl()?;
let (, ) = get_hl()?;
let (, ) = get_hl()?;
let = chanlun::types::::(, , , );
let = chanlun::types::::(, , , );
let = |d: chanlun::types::| d.();
let = |d: chanlun::types::| d.();
let result = match (, ) {
(d1, d2) if matches!(d1, chanlun::types::::) && ! => {
panic!("顺序包含: {:?} {:?}", d1, d2);
}
(d1, d2) if matches!(d2, chanlun::types::::) && ! => {
panic!("顺序包含: {:?} {:?}", d1, d2);
}
(a, b) if (a) && (b) => chanlun::types::::,
(a, b) if (a) && (b) => chanlun::types::::,
(a, chanlun::types::::) if (a) && => {
chanlun::types::::
}
(a, b) if (a) && (b) => chanlun::types::::,
(a, b) if (a) && (b) => chanlun::types::::,
(a, chanlun::types::::) if (a) && => {
chanlun::types::::
}
(chanlun::types::::, a) if (a) && => {
chanlun::types::::
}
(chanlun::types::::, a) if (a) && => {
chanlun::types::::
}
(chanlun::types::::, chanlun::types::::) if => {
chanlun::types::::
}
_ => return Ok(None),
};
Ok(Some(Self { inner: result }))
}
}
// ========== 缺口 ==========
#[pyclass(name = "缺口")]
#[derive(Clone)]
pub struct Py {
pub inner: chanlun::types::,
}
#[pymethods]
impl Py {
#[new]
fn new(: f64, : f64) -> PyResult<Self> {
if <= {
return Err(pyo3::exceptions::PyValueError::new_err(format!(
"缺口高必须大于低: 高={高}, 低={低}"
)));
}
Ok(Self {
inner: chanlun::types::::new(, ),
})
}
fn __str__(&self) -> String {
format!("{}", self.inner)
}
fn __repr__(&self) -> String {
format!("{}", self.inner)
}
#[getter]
#[pyo3(name = "")]
fn get_高(&self) -> f64 {
self.inner.
}
#[setter]
#[pyo3(name = "")]
fn set_高(&mut self, value: f64) {
self.inner. = value;
}
#[getter]
#[pyo3(name = "")]
fn get_低(&self) -> f64 {
self.inner.
}
#[setter]
#[pyo3(name = "")]
fn set_低(&mut self, value: f64) {
self.inner. = value;
}
#[classmethod]
fn (
_cls: &Bound<'_, PyType>,
: f64,
: f64,
: Option<f64>,
) -> Option<Self> {
let = .unwrap_or(0.15);
chanlun::types::::(, , ).map(|inner| Self { inner })
}
}
// ========== Registration ==========
pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
// Register classes
m.add_class::<Py>()?;
m.add_class::<Py>()?;
m.add_class::<Py>()?;
m.add_class::<Py>()?;
// 买卖点类型 class attributes (singleton instances)
let py = m.py();
let bsp_class = m.getattr("买卖点类型")?;
let bsp_class = bsp_class.downcast_into::<PyType>()?;
let variants: &[(&str, chanlun::types::)] = &[
("一买", chanlun::types::::),
("一卖", chanlun::types::::),
("二买", chanlun::types::::),
("二卖", chanlun::types::::),
("三买", chanlun::types::::),
("三卖", chanlun::types::::),
("T1买", chanlun::types::::T1买),
("T1卖", chanlun::types::::T1卖),
("T1P买", chanlun::types::::T1P买),
("T1P卖", chanlun::types::::T1P卖),
("T2买", chanlun::types::::T2买),
("T2卖", chanlun::types::::T2卖),
("T2S买", chanlun::types::::T2S买),
("T2S卖", chanlun::types::::T2S卖),
("T3A买", chanlun::types::::T3A买),
("T3A卖", chanlun::types::::T3A卖),
("T3B买", chanlun::types::::T3B买),
("T3B卖", chanlun::types::::T3B卖),
];
for (name, value) in variants {
let instance = Py::new(py, Py { inner: *value })?;
bsp_class.setattr(*name, instance)?;
}
// 相对方向 class attributes
let dir_class = m.getattr("相对方向")?.downcast_into::<PyType>()?.clone();
let dir_variants: &[(&str, chanlun::types::)] = &[
("向上", chanlun::types::::),
("向下", chanlun::types::::),
("向上缺口", chanlun::types::::),
("向下缺口", chanlun::types::::),
("衔接向上", chanlun::types::::),
("衔接向下", chanlun::types::::),
("", chanlun::types::::),
("", chanlun::types::::),
("", chanlun::types::::),
];
for (name, value) in dir_variants {
let instance = Py::new(py, Py { inner: *value })?;
dir_class.setattr(*name, instance)?;
}
// 分型结构 class attributes
let frac_class = m.getattr("分型结构")?.downcast_into::<PyType>()?.clone();
let frac_variants: &[(&str, chanlun::types::)] = &[
("", chanlun::types::::),
("", chanlun::types::::),
("", chanlun::types::::),
("", chanlun::types::::),
("", chanlun::types::::),
];
for (name, value) in frac_variants {
let instance = Py::new(py, Py { inner: *value })?;
frac_class.setattr(*name, instance)?;
}
// Register module-level functions
m.add_function(wrap_pyfunction!(, m)?)?;
m.add_function(wrap_pyfunction!(_数字, m)?)?;
Ok(())
}
// ========== Module-level functions ==========
#[pyfunction]
fn (ts: &Bound<'_, PyAny>) -> PyResult<i64> {
// Try int first
if let Ok(v) = ts.extract::<i64>() {
return Ok(v);
}
// Try float
if let Ok(v) = ts.extract::<f64>() {
return Ok(v as i64);
}
// Try string (ISO 8601 or "YYYY-MM-DD HH:MM:SS")
if let Ok(s) = ts.extract::<String>() {
return parse_datetime_string(&s);
}
// Try datetime object
if let Ok(dt) = ts.getattr("timestamp") {
let ts_float: f64 = dt.call0()?.extract()?;
return Ok(ts_float as i64);
}
Err(pyo3::exceptions::PyValueError::new_err(format!(
"无法转化为时间戳: {:?}",
ts
)))
}
#[pyfunction]
fn _数字(ts: &Bound<'_, PyAny>) -> PyResult<i64> {
(ts)
}
fn parse_datetime_string(s: &str) -> PyResult<i64> {
// Try ISO 8601: "2024-10-15T16:45:00"
if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S") {
return Ok(dt.and_utc().timestamp());
}
// Try "YYYY-MM-DD HH:MM:SS"
if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") {
return Ok(dt.and_utc().timestamp());
}
// Try "YYYY-MM-DD"
if let Ok(d) = chrono::NaiveDate::parse_from_str(s, "%Y-%m-%d") {
let dt = d
.and_hms_opt(0, 0, 0)
.ok_or_else(|| pyo3::exceptions::PyValueError::new_err(format!("无法解析日期: {s}")))?;
return Ok(dt.and_utc().timestamp());
}
Err(pyo3::exceptions::PyValueError::new_err(format!(
"无法解析时间字符串: {s}"
)))
}
+135
View File
@@ -0,0 +1,135 @@
#!/usr/bin/env python3
"""Integration test: feed .nb bars through PyO3 observer, compare with Python reference output."""
import sys
import os
import struct
import chanlun
def read_nb_bars(path, max_bars=None):
"""Read bars from .nb file (48 bytes each: 6 × f64 big-endian)."""
bars = []
with open(path, "rb") as f:
i = 0
while True:
data = f.read(48)
if not data:
break
ts, o, h, l, c, v = struct.unpack(">6d", data)
bars.append((int(ts), o, h, l, c, v))
i += 1
if max_bars and i >= max_bars:
break
return bars
def main():
nb_path = "/home/moscow/chanlun.rs/btcusd-300-1761327300-1776327900.nb"
ref_dir = "/home/moscow/chanlun.rs/Py_btcusd:300_1761327300_1776327900"
out_dir = "/tmp/chanlun_py_test_output"
# Read all bars
print("Reading bars from .nb file...")
bars = read_nb_bars(nb_path)
print(f" Read {len(bars)} bars")
# Create observer (default config)
print("Creating observer...")
obs = chanlun.观察者("btcusd", 300)
print(f" Observer: {obs.标识}, period={obs.周期}")
# Feed bars
print("Feeding bars...")
for i, (ts, o, h, l, c, v) in enumerate(bars):
k = chanlun.K线.创建普K(f"btcusd_{i}", ts, o, h, l, c, v, i, 300)
obs.增加原始K线(k)
if i % 10000 == 0:
print(f" Fed {i}/{len(bars)} bars")
print(f" Done. {len(obs.普通K线序列)} normal K lines, {len(obs.缠论K线序列)} Chan K lines")
# Save output
print(f"Saving data to {out_dir}...")
os.makedirs(out_dir, exist_ok=True)
obs.测试_保存数据(out_dir)
# Find the actual output subdirectory created by 测试_保存数据
subdirs = [d for d in os.listdir(out_dir) if os.path.isdir(os.path.join(out_dir, d))]
if not subdirs:
print("ERROR: No output subdirectory found!")
return 1
actual_out_dir = os.path.join(out_dir, subdirs[0])
out_files = sorted(os.listdir(actual_out_dir))
print(f" Output dir: {actual_out_dir}")
print(f" Output files ({len(out_files)}): {out_files}")
# Compare with Python reference
print("\nComparing with Python reference...")
ref_files = sorted(os.listdir(ref_dir))
match_count = 0
diff_count = 0
all_match = True
for fname in ref_files:
ref_path = os.path.join(ref_dir, fname)
out_path = os.path.join(actual_out_dir, fname)
if not os.path.exists(out_path):
print(f" MISSING: {fname}")
all_match = False
continue
with open(ref_path) as f:
ref_lines = f.readlines()
with open(out_path) as f:
out_lines = f.readlines()
if ref_lines == out_lines:
print(f" MATCH: {fname} ({len(ref_lines)} lines)")
match_count += 1
else:
print(f" DIFF: {fname} (ref={len(ref_lines)} lines, out={len(out_lines)} lines)")
for j, (rl, ol) in enumerate(zip(ref_lines, out_lines)):
if rl != ol:
print(f" Line {j}:")
print(f" REF: {rl.rstrip()}")
print(f" OUT: {ol.rstrip()}")
break
if len(ref_lines) != len(out_lines):
print(f" Line count differs")
diff_count += 1
all_match = False
# Also compare extra files against Rust reference
rust_ref_dir = "/home/moscow/chanlun.rs/chanlun/Rust_btcusd:300_1761327300_1776327900"
extra_files = set(out_files) - set(ref_files)
if extra_files:
print("\nComparing extra files with Rust reference...")
for fname in sorted(extra_files):
out_path = os.path.join(actual_out_dir, fname)
rust_ref_path = os.path.join(rust_ref_dir, fname)
if os.path.exists(rust_ref_path):
with open(rust_ref_path) as f:
ref_lines = f.readlines()
with open(out_path) as f:
out_lines = f.readlines()
if ref_lines == out_lines:
print(f" MATCH: {fname} (vs Rust ref, {len(ref_lines)} lines)")
match_count += 1
else:
print(f" DIFF: {fname} (vs Rust ref)")
diff_count += 1
print(f"\nSummary: {match_count} match, {diff_count} differ")
if all_match:
print("All Python reference files match!")
return 0
else:
print("Some files differ (see above)")
return 1
if __name__ == "__main__":
sys.exit(main())
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "chanlun"
version = "0.1.0"
version = "26.5.1"
edition = "2021"
[dependencies]