Files
chanlun.rs/chanlun-py/src/kline_py.rs
T
YuWuKunCheng b7c4e60420 1. thread_local! 导致跨线程缓存不可见(主要问题)
kline_py.rs 中 BSP_CACHE、KLINE_IDENTITY、BAR_IDENTITY 使用了 thread_local!。主线程调用 识别买卖点() →
  买卖点信息.add() 写入的是主线程的 PySet,backtrader 策略线程读取的是自己线程独立的空 PySet。

  修复:将三个缓存从 thread_local! 改为全局 static + std::sync::LazyLock<RwLock<HashMap<...>>>

  影响分析

  这些身份缓存的影响与 KLINE_IDENTITY/BAR_IDENTITY 不同——它们只影响 Python 对象身份(is
  比较),不直接影响数据内容(因为 Rust 数据通过 Arc 共享,读写都是同一份)。

  具体后果:
  - 不同线程访问同一个 Rust Arc 会得到不同的 Python wrapper 对象
  - a is b 跨线程比较返回 False,哪怕它们包装同一个底层 Rust 对象
  - 每个线程维护一份独立缓存,内存浪费(不过 wrapper 很小)
2026-05-30 22:15:09 +08:00

741 lines
24 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::{PyBytes, PyDict, PyType};
use std::collections::HashMap;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::RwLock;
use crate::config_py::缠论配置Py;
use crate::indicators_py::{平滑异同移动平均线Py, 相对强弱指数Py, 随机指标Py};
use crate::types_py::相对方向Py;
// ========== K线 ==========
/// 原始 K 线 — OHLCV 数据,可内嵌 MACD/RSI/KDJ 指标。
///
/// 属性(大部分可读写):
/// 标识: str / 序号: int / 周期: int (秒) / 时间戳: int (Unix秒)
/// 高: float / 低: float / 开盘价: float / 收盘价: float / 成交量: float
/// 方向: 相对方向 (只读) — 根据开盘价vs收盘价判定
/// MACD: 平滑异同移动平均线|None (只读)
/// RSI: 相对强弱指数|None (只读)
/// KDJ: 随机指标|None (只读)
///
/// 类方法:
/// 创建普K(标识, 时间戳, 开盘价, 最高价, 最低价, 收盘价, 成交量, 序号?, 周期?) -> K线
/// — 快捷构造普通K线
/// 读取大端字节数组(bytes, 周期, 标识?) -> K线
/// — 从大端字节序二进制数据反序列化(兼容 .dat/.nb 文件格式)
/// 保存到DAT文件(路径, K线序列) -> 写入 .dat 二进制文件
/// 获取MACD(K线序列, 计算方式, 快线周期?, 慢线周期?, 信号周期?) -> list[平滑异同移动平均线]
/// — 对整个K线序列批量计算 MACD
/// 截取(序列, 起点K线, 终点K线) -> list — 按时间戳截取K线区间
#[pyclass(name = "K线", module = "chanlun._chanlun")]
pub struct K线Py {
pub(crate) inner: Arc<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: Arc::new(chanlun::kline::bar::K线 {
标识: 标识.to_string(),
序号,
周期,
时间戳,
,
,
开盘价,
收盘价,
成交量,
macd: None,
rsi: None,
kdj: None,
}),
}
}
#[getter]
fn 标识(&self) -> String {
self.inner.标识.clone()
}
#[getter]
fn 序号(&self) -> i64 {
self.inner.序号
}
#[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) -> f64 {
self.inner.开盘价
}
#[getter]
fn 收盘价(&self) -> f64 {
self.inner.收盘价
}
#[getter]
fn 成交量(&self) -> f64 {
self.inner.成交量
}
#[getter]
/// :return: 相对方向.向上(开盘<收盘)或 相对方向.向下(开盘>收盘)
fn 方向(&self, py: Python<'_>) -> Py<相对方向Py> {
crate::types_py::获取相对方向单例(py, 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() })
}
/// 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("开盘价", self.开盘价())?;
dict.set_item("收盘价", self.收盘价())?;
dict.set_item("成交量", self.成交量())?;
dict.set_item("方向", self.方向(py))?;
if let Some(v) = self.macd() {
dict.set_item("macd", v)?;
}
if let Some(v) = self.rsi() {
dict.set_item("rsi", v)?;
}
if let Some(v) = self.kdj() {
dict.set_item("kdj", v)?;
}
Ok(dict.into())
}
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()
}
fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
return Arc::as_ptr(&self.inner) == Arc::as_ptr(&other.inner);
}
false
}
fn __hash__(&self) -> u64 {
Arc::as_ptr(&self.inner) as u64
}
#[classmethod]
#[pyo3(signature = (标识, 时间戳, 开盘价, 最高价, 最低价, 收盘价, 成交量, 序号 = None, 周期 = None))]
/// 快捷构造普通K线
fn 创建普K(
_cls: &Bound<'_, PyType>,
标识: &str,
时间戳: i64,
开盘价: f64,
最高价: f64,
最低价: f64,
收盘价: f64,
成交量: f64,
序号: Option<i64>,
周期: Option<i64>,
) -> Self {
Self {
inner: Arc::new(chanlun::kline::bar::K线::创建普K(
标识,
时间戳,
开盘价,
最高价,
最低价,
收盘价,
成交量,
序号.unwrap_or(0),
周期.unwrap_or(60),
)),
}
}
#[classmethod]
/// 将K线序列保存为二进制DAT文件
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.as_ref()).collect();
chanlun::kline::bar::K线::保存到DAT文件(路径, &bars)
.map_err(|e| pyo3::exceptions::PyIOError::new_err(e.to_string()))
}
#[classmethod]
/// 从大端字节序二进制数据反序列化K线(兼容.dat/.nb文件格式)
fn 读取大端字节数组(
_cls: &Bound<'_, PyType>,
字节组: &Bound<'_, PyBytes>,
周期: i64,
标识: &str,
) -> Option<Self> {
chanlun::kline::bar::K线::读取大端字节数组(字节组.as_bytes(), 周期, 标识).map(|inner| {
Self {
inner: Arc::new(inner),
}
})
}
#[classmethod]
/// 计算指定K线区间的MACD柱面积
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.as_ref()).collect();
chanlun::kline::bar::K线::获取MACD(&bars, &.borrow().inner, &.borrow().inner)
}
#[staticmethod]
/// 按起止K线截取K线子序列
fn 截取(
序列: Vec<Py<Self>>,
: &Bound<'_, Self>,
: &Bound<'_, Self>,
py: Python<'_>,
) -> PyResult<Vec<Py<Self>>> {
let start_ptr = Arc::as_ptr(&.borrow().inner);
let end_ptr = Arc::as_ptr(&.borrow().inner);
let start_ts = .borrow().inner.时间戳;
let end_ts = .borrow().inner.时间戳;
let start_idx = 序列
.iter()
.position(|k| {
Arc::as_ptr(&k.borrow(py).inner) == start_ptr
|| k.borrow(py).inner.时间戳 == start_ts
})
.ok_or_else(|| pyo3::exceptions::PyValueError::new_err("始 不在序列中"))?;
let end_idx = 序列
.iter()
.position(|k| {
Arc::as_ptr(&k.borrow(py).inner) == end_ptr || k.borrow(py).inner.时间戳 == end_ts
})
.ok_or_else(|| pyo3::exceptions::PyValueError::new_err("终 不在序列中"))?;
if start_idx > end_idx {
return Err(pyo3::exceptions::PyValueError::new_err("始 排序在 终 之后"));
}
Ok(序列
.into_iter()
.skip(start_idx)
.take(end_idx - start_idx + 1)
.collect())
}
}
// ========== 缠论K线 ==========
/// 缠论K线 — 经包含处理后的标准化K线,有方向(向上/向下)、分型结构标记。
///
/// 属性 (只读):
/// 序号: int / 时间戳: int (Unix秒) / 高: float / 低: float / 周期: int / 标识: str
/// 方向: 相对方向 — K线运行方向(向上/向下)
/// 分型: 分型结构|None — 当前K线在分型中的角色
///
/// 类方法:
/// 时间戳对齐(缠K序列, 参照K线或时间戳) -> int
/// — 在缠K序列中找到与参照最接近的时间戳
/// 创建缠K(普K, 指示器) -> 缠论K线
/// — 从普通K线创建缠论K线
/// 兼并(缠K序列, 方向, 配置, 需要特定强度?) -> list[缠论K线]
/// — 根据方向进行K线包含处理(合并)
/// 分析(缠K序列, 配置, 可以逆序包含?, 忽视顺序包含?, 可以逆序包含新?) -> (str, 分型|None)
/// — 分析分型形成结果
/// 截取(序列, 起点分型, 终点分型) -> list — 截取分型间的缠K子序列
#[pyclass(name = "缠论K线", module = "chanlun._chanlun", from_py_object)]
pub struct 缠论K线Py {
pub(crate) inner: std::sync::Arc<chanlun::kline::chan_kline::缠论K线>,
}
impl 缠论K线Py {
pub(crate) fn from_rc(inner: std::sync::Arc<chanlun::kline::chan_kline::缠论K线>) -> Self {
Self { inner }
}
}
/// 对象标识缓存:Arc 地址 → 规范 Python 对象
/// 确保同一底层 Arc 指针在 Python 侧始终映射到同一 PyObject
/// 使用全局 static 而非 thread_local!,保证跨线程对象标识和买卖点信息一致性
static BAR_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<K线Py>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
static KLINE_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<缠论K线Py>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
/// 买卖点信息缓存 — 按 Arc 指针全局共享,确保所有 wrapper 看到同一 PySet
static BSP_CACHE: std::sync::LazyLock<RwLock<HashMap<usize, Py<pyo3::types::PySet>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
/// 将 Rc<K线> 转为 Py<K线Py>,确保同一 Rc 地址总是返回同一 Python 对象
pub(crate) fn bar_to_py(
py: Python<'_>,
inner: std::sync::Arc<chanlun::kline::bar::K线>,
) -> Py<K线Py> {
let key = Arc::as_ptr(&inner) as usize;
if let Some(cached) = BAR_IDENTITY
.read()
.unwrap()
.get(&key)
.map(|p| p.clone_ref(py))
{
return cached;
}
let obj = Py::new(py, K线Py { inner }).unwrap();
BAR_IDENTITY.write().unwrap().insert(key, obj.clone_ref(py));
obj
}
/// 将 Rc<缠论K线> 转为 Py<缠论K线Py>,确保同一 Rc 地址总是返回同一 Python 对象
pub(crate) fn chan_kline_to_py(
py: Python<'_>,
inner: std::sync::Arc<chanlun::kline::chan_kline::缠论K线>,
) -> Py<缠论K线Py> {
let key = Arc::as_ptr(&inner) as usize;
if let Some(cached) = KLINE_IDENTITY
.read()
.unwrap()
.get(&key)
.map(|p| p.clone_ref(py))
{
return cached;
}
let obj = Py::new(py, 缠论K线Py::from_rc(inner)).unwrap();
KLINE_IDENTITY
.write()
.unwrap()
.insert(key, obj.clone_ref(py));
obj
}
impl Clone for 缠论K线Py {
fn clone(&self) -> Self {
Self {
inner: std::sync::Arc::clone(&self.inner),
}
}
}
#[pymethods]
impl 缠论K线Py {
#[new]
fn new() -> Self {
unimplemented!("使用 创建缠K 创建")
}
#[getter]
fn 序号(&self) -> i64 {
self.inner.序号.load(Ordering::Relaxed)
}
#[getter]
fn 时间戳(&self) -> i64 {
self.inner.时间戳.load(Ordering::Relaxed)
}
#[getter]
fn (&self) -> f64 {
self.inner..get()
}
#[getter]
fn (&self) -> f64 {
self.inner..get()
}
#[getter]
fn 方向(&self, py: Python<'_>) -> Py<相对方向Py> {
crate::types_py::获取相对方向单例(py, *self.inner.方向.read().unwrap())
}
#[getter]
fn 分型(&self, py: Python<'_>) -> Option<Py<crate::types_py::分型结构Py>> {
self.inner
.分型
.read()
.unwrap()
.map(|f| crate::types_py::获取分型结构单例(py, f))
}
#[getter]
fn 周期(&self) -> i64 {
self.inner.周期
}
#[getter]
fn 标识(&self) -> String {
self.inner.标识.clone()
}
#[getter]
fn 分型特征值(&self) -> f64 {
self.inner.分型特征值.get()
}
#[getter]
fn 原始起始序号(&self) -> i64 {
self.inner.原始起始序号
}
#[getter]
fn 原始结束序号(&self) -> i64 {
self.inner.原始结束序号.load(Ordering::Relaxed)
}
#[getter]
fn 标的K线(&self, py: Python<'_>) -> Py<K线Py> {
bar_to_py(py, self.inner.标的K线.read().unwrap().clone())
}
/// 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.方向(py))?;
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("与RSI匹配", self.RSI匹配())?;
dict.set_item("与KDJ匹配", self.KDJ匹配())?;
if let Some(v) = self.分型(py) {
dict.set_item("分型", v)?;
}
Ok(dict.into())
}
fn __str__(&self) -> String {
format!("{}", self.inner)
}
fn __repr__(&self) -> String {
self.__str__()
}
fn __eq__(&self, other: &Bound<'_, PyAny>) -> bool {
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
return Arc::as_ptr(&self.inner) == Arc::as_ptr(&other.inner);
}
false
}
fn __hash__(&self) -> u64 {
Arc::as_ptr(&self.inner) as u64
}
#[getter]
/// 创建当前缠K的浅拷贝副本
fn 镜像(&self, py: Python<'_>) -> Self {
let mirror = Self {
inner: std::sync::Arc::new(self.inner.镜像()),
};
// 复制买卖点信息到镜像
let src_key = Arc::as_ptr(&self.inner) as usize;
let dst_key = Arc::as_ptr(&mirror.inner) as usize;
let cached_src = BSP_CACHE
.read()
.unwrap()
.get(&src_key)
.map(|p| p.clone_ref(py));
if let Some(cached_src) = cached_src {
if let Ok(new_set) = pyo3::types::PySet::empty(py) {
for item in cached_src.bind(py).iter() {
let _ = new_set.add(item);
}
let py_set: Py<pyo3::types::PySet> = new_set.into();
BSP_CACHE.write().unwrap().insert(dst_key, py_set);
}
}
mirror
}
#[getter]
/// :return: 底分型时MACD柱<0,顶分型时MACD柱>0
fn MACD柱子匹配(&self) -> bool {
self.inner.MACD柱子匹配()
}
#[getter]
/// :return: 底分型时RSI < RSI_SMA,顶分型时RSI > RSI_SMA
fn RSI匹配(&self) -> bool {
self.inner.RSI匹配()
}
#[getter]
/// :return: 底分型时K<D,顶分型时K>D
fn KDJ匹配(&self) -> bool {
self.inner.KDJ匹配()
}
#[getter]
fn 买卖点信息(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let key = Arc::as_ptr(&self.inner) as usize;
// 检查全局缓存
let cached = BSP_CACHE.read().unwrap().get(&key).map(|p| p.clone_ref(py));
if let Some(set) = cached {
return Ok(set.into_any());
}
// 创建新的 PySet 并存入全局缓存
let set = pyo3::types::PySet::empty(py)?;
BSP_CACHE.write().unwrap().insert(key, set.into());
// 重新读取并返回(无法从 insert 获取 Py 引用,需要重新读)
Ok(BSP_CACHE
.read()
.unwrap()
.get(&key)
.unwrap()
.clone_ref(py)
.into_any())
}
#[classmethod]
/// 在基线序列中找到与k线时间戳对齐的时间戳
fn 时间戳对齐(
_cls: &Bound<'_, PyType>,
基线: Vec<Py<Self>>,
k线: &Bound<'_, Self>,
py: Python<'_>,
) -> i64 {
let rc_list: Vec<_> = 基线
.iter()
.map(|k| std::sync::Arc::clone(&k.bind(py).borrow().inner))
.collect();
chanlun::kline::chan_kline::缠论K线::时间戳对齐(&rc_list, &k线.borrow().inner)
}
#[classmethod]
#[pyo3(signature = (时间戳, 高, 低, 方向, 结构, 原始序号, 普k, 之前 = None))]
/// 创建新的缠论K线
fn 创建缠K(
_cls: &Bound<'_, PyType>,
时间戳: i64,
: f64,
: f64,
方向: &Bound<'_, 相对方向Py>,
结构: Option<&Bound<'_, crate::types_py::分型结构Py>>,
原始序号: i64,
k: &Bound<'_, K线Py>,
之前: Option<&Bound<'_, Self>>,
py: Python<'_>,
) -> Py<Self> {
let prev_ref = 之前.map(|prev| prev.borrow());
let prev_inner = prev_ref.as_ref().map(|r| r.inner.as_ref());
let inner = chanlun::kline::chan_kline::缠论K线::创建缠K(
时间戳,
,
,
方向.borrow().inner,
结构.map(|s| s.borrow().inner),
原始序号,
k.borrow().inner.clone(),
prev_inner,
);
chan_kline_to_py(py, std::sync::Arc::new(inner))
}
#[classmethod]
/// K线包含处理(合并)
fn 兼并(
_cls: &Bound<'_, PyType>,
之前缠K: Option<&Bound<'_, Self>>,
当前缠K: &Bound<'_, Self>,
当前普K: &Bound<'_, K线Py>,
配置: &Bound<'_, 缠论配置Py>,
py: Python<'_>,
) -> PyResult<(Option<Py<Self>>, Option<String>)> {
let 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,
&ck_inner,
&当前普K.borrow().inner,
&config,
);
Ok((result.map(|rc| chan_kline_to_py(py, rc)), mode))
}
#[classmethod]
/// 分析K线,执行指标计算+包含处理+分型判定
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::sync::Arc::clone(&k.bind(py).borrow().inner))
.collect();
let mut bar_seq: Vec<_> = K序列
.iter()
.map(|k| 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]
/// :param 序列: 缠K序列
fn 截取(
序列: Vec<Py<Self>>,
: &Bound<'_, Self>,
: &Bound<'_, Self>,
py: Python<'_>,
) -> PyResult<Vec<Py<Self>>> {
let start_ptr = Arc::as_ptr(&.borrow().inner);
let end_ptr = Arc::as_ptr(&.borrow().inner);
let start_ts = .borrow().inner.时间戳.load(Ordering::Relaxed);
let end_ts = .borrow().inner.时间戳.load(Ordering::Relaxed);
let start_idx = 序列
.iter()
.position(|k| {
Arc::as_ptr(&k.borrow(py).inner) == start_ptr
|| k.borrow(py).inner.时间戳.load(Ordering::Relaxed) == start_ts
})
.ok_or_else(|| pyo3::exceptions::PyValueError::new_err("始 不在序列中"))?;
let end_idx = 序列
.iter()
.position(|k| {
Arc::as_ptr(&k.borrow(py).inner) == end_ptr
|| k.borrow(py).inner.时间戳.load(Ordering::Relaxed) == end_ts
})
.ok_or_else(|| pyo3::exceptions::PyValueError::new_err("终 不在序列中"))?;
if start_idx > end_idx {
return Err(pyo3::exceptions::PyValueError::new_err("始 排序在 终 之后"));
}
Ok(序列
.into_iter()
.skip(start_idx)
.take(end_idx - start_idx + 1)
.collect())
}
}
pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<K线Py>()?;
m.add_class::<缠论K线Py>()?;
Ok(())
}