Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c3e9ae8d77 | |||
| b7c4e60420 | |||
| 15dc44e8e0 | |||
| bd4fceab02 | |||
| af3ebf13c8 | |||
| 22109d8b30 |
@@ -71,7 +71,7 @@ jobs:
|
||||
- name: Lint 检查
|
||||
if: steps.check.outputs.exists == 'false'
|
||||
working-directory: chanlun
|
||||
run: cargo clippy -- -D warnings
|
||||
run: cargo clippy
|
||||
|
||||
- name: 运行测试
|
||||
if: steps.check.outputs.exists == 'false'
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
- name: 验证打包
|
||||
if: steps.check.outputs.exists == 'false'
|
||||
working-directory: chanlun
|
||||
run: cargo package --dry-run
|
||||
run: cargo publish --dry-run --allow-dirty
|
||||
|
||||
- name: 登录 crates.io
|
||||
if: steps.check.outputs.exists == 'false'
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
- name: 发布 chanlun 至 crates.io
|
||||
if: steps.check.outputs.exists == 'false'
|
||||
working-directory: chanlun
|
||||
run: cargo publish
|
||||
run: cargo publish --allow-dirty
|
||||
|
||||
# ============================================================
|
||||
# 2. 构建 wheel — Linux x86_64 (manylinux)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[](https://pypi.org/project/chanlun/)
|
||||
[](LICENSE)
|
||||
|
||||
基于 [chanlun](./chanlun/) Rust 核心库的 PyO3 高性能 Python 绑定,API 与 `chan.py` 完全兼容。
|
||||
基于 [chanlun](./chanlun/) Rust 核心库的 PyO3 高性能 Python 绑定,API 参考 `chan.py` 设计,高度兼容。
|
||||
|
||||
## 安装
|
||||
|
||||
@@ -19,8 +19,8 @@ import chanlun
|
||||
# 创建配置(全部默认值)
|
||||
config = chanlun.缠论配置()
|
||||
|
||||
# 读取 K 线数据文件,创建观察者
|
||||
obs = chanlun.观察者.读取数据文件("path/to/data.nb", config)
|
||||
# 读取 K 线数据文件(文件名需遵循 `符号-周期-起始时间戳-结束时间戳.nb` 格式,如 `btcusd-300-1631772074-1632222374.nb`)
|
||||
obs = chanlun.观察者.读取数据文件("path/to/btcusd-300-1631772074-1632222374.nb", config)
|
||||
|
||||
# 查看各层级序列
|
||||
print(f"K线数量: {len(obs.普通K线序列)}")
|
||||
@@ -29,7 +29,7 @@ print(f"线段数量: {len(obs.线段序列)}")
|
||||
print(f"中枢数量: {len(obs.中枢序列)}")
|
||||
|
||||
# 或使用立体分析器进行多周期分析
|
||||
analyzer = chanlun.立体分析器("BTCUSD", ["1min", "5min", "30min"], config)
|
||||
analyzer = chanlun.立体分析器("BTCUSD", [60, 60*5, 60*5*6], config)
|
||||
# 逐根投喂 K 线...
|
||||
```
|
||||
|
||||
@@ -53,7 +53,6 @@ pip install target/wheels/chanlun-*.whl
|
||||
```bash
|
||||
./build.sh develop # 开发安装
|
||||
./build.sh wheel # 构建 wheel
|
||||
./build.sh test # 运行集成测试
|
||||
```
|
||||
|
||||
## 导出类
|
||||
@@ -70,8 +69,8 @@ pip install target/wheels/chanlun-*.whl
|
||||
## 兼容性
|
||||
|
||||
- Python 3.9+
|
||||
- 类名 / 方法名 / 字段名 / 签名与 `chan.py` 一致
|
||||
- 支持 `.nb` / `.dat` 二进制文件格式(大端字节序)
|
||||
- 类名 / 方法名 / 字段名与 `chan.py` 保持一致
|
||||
- 支持 `.nb` 二进制文件格式(大端字节序)
|
||||
|
||||
## 许可
|
||||
|
||||
|
||||
@@ -5243,12 +5243,12 @@ class 观察者:
|
||||
buffer = f.read()
|
||||
size = struct.calcsize(">6d")
|
||||
for i in range(len(buffer) // size):
|
||||
时间戳, 开盘价, 最高价, 最低价, 收盘价, 成交量 = struct.unpack(">6d", buffer[i * size: i * size + size])
|
||||
时间戳, 开盘价, 最高价, 最低价, 收盘价, 成交量 = struct.unpack(">6d", buffer[i * size : i * size + size])
|
||||
self.投喂原始数据(转化为时间戳(int(时间戳)), 开盘价, 最高价, 最低价, 收盘价, 成交量)
|
||||
|
||||
@classmethod
|
||||
def 读取数据文件(cls,观察员:"观察者", 文件路径: str, 配置=缠论配置()) -> Self:
|
||||
""" 加载数据文件
|
||||
def 读取数据文件(cls, 观察员: "观察者", 文件路径: str, 配置=缠论配置()) -> Self:
|
||||
"""加载数据文件
|
||||
:param 观察员: 观察者
|
||||
:param 文件路径: 数据文件路径 格式如: btcusd-300-1631772074-1632222374.nb
|
||||
:param 配置: 缠论配置
|
||||
@@ -5513,7 +5513,7 @@ class 立体分析器:
|
||||
print(f"多级别数据拆分保存完成,目录:{保存路径.resolve()}")
|
||||
|
||||
|
||||
def 测试_读取数据(观察员: 观察者, 配置: 缠论配置)-> Callable[[], "观察者"]:
|
||||
def 测试_读取数据(观察员: 观察者, 配置: 缠论配置) -> Callable[[], "观察者"]:
|
||||
"""测试_读取数据
|
||||
:param 观察员: 观察者
|
||||
:param 配置: 缠论配置
|
||||
@@ -5563,5 +5563,5 @@ if __name__ == "__main__":
|
||||
当前配置 = 缠论配置.不推送()
|
||||
当前配置.加载文件路径 = str(Path(__file__).parent / "btcusd-300-1761327300-1776327900.nb")
|
||||
观察员 = 观察者("", 0, 当前配置)
|
||||
测试_读取数据(观察员,当前配置)()# .测试_保存数据()
|
||||
测试_读取数据(观察员, 当前配置)() # .测试_保存数据()
|
||||
# 测试_周期合成(当前配置)().测试_保存数据()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "chanlun-py"
|
||||
version = "26.5.101"
|
||||
version = "26.5.103"
|
||||
edition = "2021"
|
||||
description = "缠论技术分析库 — Rust 高性能 Python 绑定"
|
||||
authors = ["YuYuKunKun"]
|
||||
@@ -12,7 +12,7 @@ crate-type = ["cdylib"]
|
||||
name = "chanlun"
|
||||
|
||||
[dependencies]
|
||||
chanlun = "26.5.5" # { path = "../chanlun" }
|
||||
chanlun = "26.5.6" # { path = "../chanlun" }
|
||||
pyo3 = { version = "0.28", features = ["experimental-inspect"] }
|
||||
serde_json = "1"
|
||||
chrono = "0.4"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../README.md
|
||||
@@ -0,0 +1,77 @@
|
||||
# chanlun — 缠论技术分析 Python 绑定
|
||||
|
||||
[](https://pypi.org/project/chanlun/)
|
||||
[](LICENSE)
|
||||
|
||||
基于 [chanlun](../chanlun/) Rust 核心库的 PyO3 高性能 Python 绑定,API 参考 `chan.py` 设计,高度兼容。
|
||||
|
||||
## 安装
|
||||
|
||||
```bash
|
||||
pip install chanlun
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
```python
|
||||
import chanlun
|
||||
|
||||
# 创建配置(全部默认值)
|
||||
config = chanlun.缠论配置()
|
||||
|
||||
# 读取 K 线数据文件(文件名需遵循 `符号-周期-起始时间戳-结束时间戳.nb` 格式,如 `btcusd-300-1631772074-1632222374.nb`)
|
||||
obs = chanlun.观察者.读取数据文件("path/to/btcusd-300-1631772074-1632222374.nb", config)
|
||||
|
||||
# 查看各层级序列
|
||||
print(f"K线数量: {len(obs.普通K线序列)}")
|
||||
print(f"笔数量: {len(obs.笔序列)}")
|
||||
print(f"线段数量: {len(obs.线段序列)}")
|
||||
print(f"中枢数量: {len(obs.中枢序列)}")
|
||||
|
||||
# 或使用立体分析器进行多周期分析
|
||||
analyzer = chanlun.立体分析器("BTCUSD", [60, 60*5, 60*5*6], config)
|
||||
# 逐根投喂 K 线...
|
||||
```
|
||||
|
||||
## 从源码构建
|
||||
|
||||
前置依赖: [Rust](https://www.rust-lang.org) + [maturin](https://www.maturin.rs)
|
||||
|
||||
```bash
|
||||
pip install maturin
|
||||
|
||||
# 开发模式(直接安装到当前 venv)
|
||||
maturin develop
|
||||
|
||||
# 或构建 wheel
|
||||
maturin build --release
|
||||
pip install target/wheels/chanlun-*.whl
|
||||
```
|
||||
|
||||
也可使用项目内的 `build.sh`:
|
||||
|
||||
```bash
|
||||
./build.sh develop # 开发安装
|
||||
./build.sh wheel # 构建 wheel
|
||||
```
|
||||
|
||||
## 导出类
|
||||
|
||||
| 类别 | 类名 | 说明 |
|
||||
|------|------|------|
|
||||
| 枚举 | `买卖点类型`, `相对方向`, `分型结构` | 缠论基础枚举 |
|
||||
| 数据 | `缺口`, `K线`, `缠论K线` | K 线数据结构 |
|
||||
| 结构 | `分型`, `虚线`, `线段特征`, `特征分型` | 分析层级结构 |
|
||||
| 指标 | `平滑异同移动平均线`, `相对强弱指数`, `随机指标` | MACD/RSI/KDJ |
|
||||
| 算法 | `笔`, `线段`, `中枢`, `背驰分析` | 识别算法 |
|
||||
| 业务 | `缠论配置`, `基础买卖点`, `买卖点`, `观察者`, `K线合成器`, `立体分析器` | 分析框架 |
|
||||
|
||||
## 兼容性
|
||||
|
||||
- Python 3.9+
|
||||
- 类名 / 方法名 / 字段名与 `chan.py` 保持一致
|
||||
- 支持 `.nb` 二进制文件格式(大端字节序)
|
||||
|
||||
## 许可
|
||||
|
||||
MIT
|
||||
@@ -5243,12 +5243,12 @@ class 观察者:
|
||||
buffer = f.read()
|
||||
size = struct.calcsize(">6d")
|
||||
for i in range(len(buffer) // size):
|
||||
时间戳, 开盘价, 最高价, 最低价, 收盘价, 成交量 = struct.unpack(">6d", buffer[i * size: i * size + size])
|
||||
时间戳, 开盘价, 最高价, 最低价, 收盘价, 成交量 = struct.unpack(">6d", buffer[i * size : i * size + size])
|
||||
self.投喂原始数据(转化为时间戳(int(时间戳)), 开盘价, 最高价, 最低价, 收盘价, 成交量)
|
||||
|
||||
@classmethod
|
||||
def 读取数据文件(cls,观察员:"观察者", 文件路径: str, 配置=缠论配置()) -> Self:
|
||||
""" 加载数据文件
|
||||
def 读取数据文件(cls, 观察员: "观察者", 文件路径: str, 配置=缠论配置()) -> Self:
|
||||
"""加载数据文件
|
||||
:param 观察员: 观察者
|
||||
:param 文件路径: 数据文件路径 格式如: btcusd-300-1631772074-1632222374.nb
|
||||
:param 配置: 缠论配置
|
||||
@@ -5513,7 +5513,7 @@ class 立体分析器:
|
||||
print(f"多级别数据拆分保存完成,目录:{保存路径.resolve()}")
|
||||
|
||||
|
||||
def 测试_读取数据(观察员: 观察者, 配置: 缠论配置)-> Callable[()]:
|
||||
def 测试_读取数据(观察员: 观察者, 配置: 缠论配置) -> Callable[()]:
|
||||
"""测试_读取数据
|
||||
:param 观察员: 观察者
|
||||
:param 配置: 缠论配置
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "chanlun"
|
||||
version = "2605.101"
|
||||
version = "2605.103"
|
||||
description = "缠论技术分析库 — Rust 高性能实现"
|
||||
readme = { file = "README.md", content-type = "text/markdown" }
|
||||
license = { file = "LICENSE", content-type = "text/plain" }
|
||||
|
||||
@@ -31,26 +31,28 @@ use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
thread_local! {
|
||||
static HUB_IDENTITY: RwLock<HashMap<usize, Py<中枢Py>>> = RwLock::new(HashMap::new());
|
||||
}
|
||||
// 使用全局 static 而非 thread_local!,保证跨线程对象标识一致性
|
||||
static HUB_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<中枢Py>>>> =
|
||||
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||
|
||||
pub(crate) fn hub_to_py(
|
||||
py: Python<'_>, inner: Arc<chanlun::algorithm::hub::中枢>
|
||||
) -> Py<中枢Py> {
|
||||
let key = Arc::as_ptr(&inner) as usize;
|
||||
if let Some(cached) =
|
||||
HUB_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
||||
if let Some(cached) = HUB_IDENTITY
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&key)
|
||||
.map(|p| p.clone_ref(py))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
HUB_IDENTITY.with(|c| {
|
||||
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
|
||||
});
|
||||
HUB_IDENTITY
|
||||
.write()
|
||||
.unwrap()
|
||||
.retain(|_, v| v.get_refcnt(py) > 1);
|
||||
let obj = Py::new(py, 中枢Py { inner }).unwrap();
|
||||
HUB_IDENTITY.with(|c| {
|
||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
||||
});
|
||||
HUB_IDENTITY.write().unwrap().insert(key, obj.clone_ref(py));
|
||||
obj
|
||||
}
|
||||
|
||||
@@ -362,13 +364,13 @@ impl 笔Py {
|
||||
笔序列: Vec<Py<虚线Py>>,
|
||||
文: &Bound<'_, 分型Py>,
|
||||
py: Python<'_>,
|
||||
) -> Option<虚线Py> {
|
||||
) -> Option<Py<虚线Py>> {
|
||||
let bi_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
.iter()
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::bi::笔::以文会友(&bi_list, &文.borrow().inner)
|
||||
.map(|inner| 虚线Py { inner })
|
||||
.map(|inner| dashed_to_py(py, inner))
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
@@ -378,13 +380,13 @@ impl 笔Py {
|
||||
笔序列: Vec<Py<虚线Py>>,
|
||||
武: &Bound<'_, 分型Py>,
|
||||
py: Python<'_>,
|
||||
) -> Option<虚线Py> {
|
||||
) -> Option<Py<虚线Py>> {
|
||||
let bi_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
.iter()
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::bi::笔::以武会友(&bi_list, &武.borrow().inner)
|
||||
.map(|inner| 虚线Py { inner })
|
||||
.map(|inner| dashed_to_py(py, inner))
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
@@ -396,13 +398,13 @@ impl 笔Py {
|
||||
缠K: &Bound<'_, crate::kline_py::缠论K线Py>,
|
||||
偏移: i64,
|
||||
py: Python<'_>,
|
||||
) -> Option<虚线Py> {
|
||||
) -> Option<Py<虚线Py>> {
|
||||
let bi_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 笔序列
|
||||
.iter()
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::bi::笔::根据缠K找笔(&bi_list, &缠K.borrow().inner, 偏移)
|
||||
.map(|inner| 虚线Py { inner })
|
||||
.map(|inner| dashed_to_py(py, inner))
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
@@ -501,7 +503,7 @@ impl 笔Py {
|
||||
) -> bool {
|
||||
let obs = 观察员.borrow();
|
||||
let obs_ref = obs.obs();
|
||||
chanlun::algorithm::bi::笔::自检(&筆.borrow().inner, &*obs_ref)
|
||||
chanlun::algorithm::bi::笔::自检(&筆.borrow().inner, &obs_ref)
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
@@ -510,12 +512,13 @@ impl 笔Py {
|
||||
_cls: &Bound<'_, PyType>,
|
||||
筆: &Bound<'_, 虚线Py>,
|
||||
观察员: &Bound<'_, 观察者Py>,
|
||||
) -> Vec<虚线Py> {
|
||||
py: Python<'_>,
|
||||
) -> Vec<Py<虚线Py>> {
|
||||
let obs = 观察员.borrow();
|
||||
let obs_ref = obs.obs();
|
||||
chanlun::algorithm::bi::笔::获取所有停顿位置(&筆.borrow().inner, &*obs_ref)
|
||||
chanlun::algorithm::bi::笔::获取所有停顿位置(&筆.borrow().inner, &obs_ref)
|
||||
.into_iter()
|
||||
.map(|d| 虚线Py { inner: Arc::new(d) })
|
||||
.map(|d| dashed_to_py(py, Arc::new(d)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -529,7 +532,7 @@ impl 笔Py {
|
||||
) -> Vec<Py<缠论K线Py>> {
|
||||
let obs = 观察员.borrow();
|
||||
let obs_ref = obs.obs();
|
||||
chanlun::algorithm::bi::笔::是否背驰过(&当前筆.borrow().inner, &*obs_ref)
|
||||
chanlun::algorithm::bi::笔::是否背驰过(&当前筆.borrow().inner, &obs_ref)
|
||||
.into_iter()
|
||||
.map(|ck| chan_kline_to_py(py, ck))
|
||||
.collect()
|
||||
@@ -569,8 +572,8 @@ impl 线段Py {
|
||||
筆: &Bound<'_, 虚线Py>,
|
||||
) -> PyResult<()> {
|
||||
let bi_rc = Arc::clone(&筆.borrow().inner);
|
||||
let mut ref_mut = 段.borrow_mut();
|
||||
chanlun::algorithm::segment::线段::添加虚线(&mut ref_mut.inner, bi_rc);
|
||||
let ref_mut = 段.borrow_mut();
|
||||
chanlun::algorithm::segment::线段::添加虚线(&ref_mut.inner, bi_rc);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -582,9 +585,9 @@ impl 线段Py {
|
||||
武: &Bound<'_, 分型Py>,
|
||||
行号: u32,
|
||||
) -> PyResult<()> {
|
||||
let mut ref_mut = 段.borrow_mut();
|
||||
let ref_mut = 段.borrow_mut();
|
||||
chanlun::algorithm::segment::线段::武斗(
|
||||
&mut ref_mut.inner,
|
||||
&ref_mut.inner,
|
||||
&Arc::clone(&武.borrow().inner),
|
||||
行号,
|
||||
);
|
||||
@@ -594,8 +597,8 @@ impl 线段Py {
|
||||
#[classmethod]
|
||||
/// 武终
|
||||
fn 武终(_cls: &Bound<'_, PyType>, 段: &Bound<'_, 虚线Py>, 行号: u32) -> PyResult<()> {
|
||||
let mut ref_mut = 段.borrow_mut();
|
||||
chanlun::algorithm::segment::线段::武终(&mut ref_mut.inner, 行号);
|
||||
let ref_mut = 段.borrow_mut();
|
||||
chanlun::algorithm::segment::线段::武终(&ref_mut.inner, 行号);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -607,12 +610,12 @@ impl 线段Py {
|
||||
序列: Vec<Py<虚线Py>>,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<()> {
|
||||
let mut ref_mut = 段.borrow_mut();
|
||||
let ref_mut = 段.borrow_mut();
|
||||
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 序列
|
||||
.iter()
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::segment::线段::验证序列(&mut ref_mut.inner, &rc_list);
|
||||
chanlun::algorithm::segment::线段::验证序列(&ref_mut.inner, &rc_list);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -624,12 +627,12 @@ impl 线段Py {
|
||||
序列: Vec<Py<虚线Py>>,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<()> {
|
||||
let mut ref_mut = 段.borrow_mut();
|
||||
let ref_mut = 段.borrow_mut();
|
||||
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 序列
|
||||
.iter()
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
.collect();
|
||||
chanlun::algorithm::segment::线段::序列重置(&mut ref_mut.inner, &rc_list);
|
||||
chanlun::algorithm::segment::线段::序列重置(&ref_mut.inner, &rc_list);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -692,7 +695,7 @@ impl 线段Py {
|
||||
let seq: Vec<Option<Arc<chanlun::structure::segment_feat::线段特征>>> = if 序列.is_none()
|
||||
{
|
||||
vec![]
|
||||
} else if let Ok(list) = 序列.downcast::<pyo3::types::PyList>() {
|
||||
} else if let Ok(list) = 序列.cast::<pyo3::types::PyList>() {
|
||||
let mut result = Vec::with_capacity(list.len());
|
||||
for item in list.iter() {
|
||||
if item.is_none() {
|
||||
@@ -708,8 +711,8 @@ impl 线段Py {
|
||||
"序列 必须是 list 或 None",
|
||||
));
|
||||
};
|
||||
let mut ref_mut = 段.borrow_mut();
|
||||
chanlun::algorithm::segment::线段::设置特征序列(&mut ref_mut.inner, seq, 行号);
|
||||
let ref_mut = 段.borrow_mut();
|
||||
chanlun::algorithm::segment::线段::设置特征序列(&ref_mut.inner, seq, 行号);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -721,22 +724,26 @@ impl 线段Py {
|
||||
配置: &Bound<'_, 缠论配置Py>,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<()> {
|
||||
let mut ref_mut = 段.borrow_mut();
|
||||
let ref_mut = 段.borrow_mut();
|
||||
let config = 配置.borrow().to_rust_config(py)?;
|
||||
chanlun::algorithm::segment::线段::刷新特征序列(&mut ref_mut.inner, &config);
|
||||
chanlun::algorithm::segment::线段::刷新特征序列(&ref_mut.inner, &config);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
/// 查找贯穿伤
|
||||
fn 查找贯穿伤(_cls: &Bound<'_, PyType>, 段: &Bound<'_, 虚线Py>) -> Option<虚线Py> {
|
||||
fn 查找贯穿伤(
|
||||
_cls: &Bound<'_, PyType>, 段: &Bound<'_, 虚线Py>
|
||||
) -> Option<Py<虚线Py>> {
|
||||
let py = _cls.py();
|
||||
chanlun::algorithm::segment::线段::查找贯穿伤(&段.borrow().inner)
|
||||
.map(|inner| 虚线Py { inner })
|
||||
.map(|inner| dashed_to_py(py, inner))
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
#[pyo3(signature = (段, 所属中枢 = None))]
|
||||
/// 将线段基础序列分割为前/后/第三买卖/贯穿伤四部分
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn 分割序列(
|
||||
_cls: &Bound<'_, PyType>,
|
||||
段: &Bound<'_, 虚线Py>,
|
||||
@@ -761,19 +768,10 @@ impl 线段Py {
|
||||
} else {
|
||||
chanlun::algorithm::segment::线段::分割序列(&borrowed.inner, None)
|
||||
};
|
||||
let wrap = |v: Vec<Arc<chanlun::structure::dash_line::虚线>>| -> PyResult<Vec<Py<虚线Py>>> {
|
||||
let mut result = Vec::new();
|
||||
for x in v {
|
||||
result.push(Py::new(py, 虚线Py { inner: x })?);
|
||||
}
|
||||
Ok(result)
|
||||
let wrap = |v: Vec<Arc<chanlun::structure::dash_line::虚线>>| -> Vec<Py<虚线Py>> {
|
||||
v.into_iter().map(|x| dashed_to_py(py, x)).collect()
|
||||
};
|
||||
Ok((
|
||||
wrap(a)?,
|
||||
wrap(b)?,
|
||||
wrap(c)?,
|
||||
d.map(|x| Py::new(py, 虚线Py { inner: x })).transpose()?,
|
||||
))
|
||||
Ok((wrap(a), wrap(b), wrap(c), d.map(|x| dashed_to_py(py, x))))
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
@@ -784,9 +782,9 @@ impl 线段Py {
|
||||
配置: &Bound<'_, 缠论配置Py>,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<()> {
|
||||
let mut ref_mut = 段.borrow_mut();
|
||||
let ref_mut = 段.borrow_mut();
|
||||
let config = 配置.borrow().to_rust_config(py)?;
|
||||
chanlun::algorithm::segment::线段::刷新(&mut ref_mut.inner, &config);
|
||||
chanlun::algorithm::segment::线段::刷新(&ref_mut.inner, &config);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -798,16 +796,14 @@ impl 线段Py {
|
||||
配置: &Bound<'_, 缠论配置Py>,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<(Py<PyAny>, Py<PyAny>, Py<PyAny>)> {
|
||||
let mut ref_mut = 段.borrow_mut();
|
||||
let ref_mut = 段.borrow_mut();
|
||||
let config = 配置.borrow().to_rust_config(py)?;
|
||||
let (a, b, c) = chanlun::algorithm::segment::线段::获取内部中枢序列(
|
||||
&mut ref_mut.inner,
|
||||
&config,
|
||||
);
|
||||
let (a, b, c) =
|
||||
chanlun::algorithm::segment::线段::获取内部中枢序列(&ref_mut.inner, &config);
|
||||
let pk_list = |v: Vec<Arc<chanlun::algorithm::hub::中枢>>| -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for h in v {
|
||||
list.append(Py::new(py, 中枢Py { inner: h })?)?;
|
||||
list.append(hub_to_py(py, h))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
};
|
||||
@@ -818,7 +814,7 @@ impl 线段Py {
|
||||
/// 内部方法:向线段序列添加新线段
|
||||
fn _添加线段(
|
||||
_cls: &Bound<'_, PyType>,
|
||||
线段序列: &Bound<'_, PyAny>,
|
||||
_线段序列: &Bound<'_, PyAny>,
|
||||
待添加线段: &Bound<'_, 虚线Py>,
|
||||
配置: &Bound<'_, 缠论配置Py>,
|
||||
行号: String,
|
||||
@@ -839,12 +835,12 @@ impl 线段Py {
|
||||
/// 内部方法:从线段序列弹出最后一个线段
|
||||
fn _弹出线段(
|
||||
_cls: &Bound<'_, PyType>,
|
||||
线段序列: &Bound<'_, PyAny>,
|
||||
_线段序列: &Bound<'_, PyAny>,
|
||||
待弹出线段: &Bound<'_, 虚线Py>,
|
||||
配置: &Bound<'_, 缠论配置Py>,
|
||||
行号: String,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<Option<虚线Py>> {
|
||||
) -> PyResult<Option<Py<虚线Py>>> {
|
||||
let config = 配置.borrow().to_rust_config(py)?;
|
||||
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = vec![];
|
||||
let result = chanlun::algorithm::segment::线段::_弹出线段(
|
||||
@@ -853,7 +849,7 @@ impl 线段Py {
|
||||
&config,
|
||||
行号,
|
||||
);
|
||||
Ok(result.map(|inner| 虚线Py { inner }))
|
||||
Ok(result.map(|inner| dashed_to_py(py, inner)))
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
@@ -1007,7 +1003,7 @@ impl 线段Py {
|
||||
待弹出线段: &Bound<'_, 虚线Py>,
|
||||
行号: u32,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<Option<虚线Py>> {
|
||||
) -> PyResult<Option<Py<虚线Py>>> {
|
||||
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::虚线>> = 线段序列
|
||||
.iter()
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
@@ -1017,7 +1013,7 @@ impl 线段Py {
|
||||
&Arc::clone(&待弹出线段.borrow().inner),
|
||||
行号,
|
||||
);
|
||||
Ok(result.map(|inner| 虚线Py { inner }))
|
||||
Ok(result.map(|inner| dashed_to_py(py, inner)))
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
@@ -1053,7 +1049,7 @@ impl 线段Py {
|
||||
let obs_ref = obs.obs();
|
||||
chanlun::algorithm::segment::线段::判断线段内部是否背驰(
|
||||
&当前段.borrow().inner,
|
||||
&*obs_ref,
|
||||
&obs_ref,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1063,12 +1059,13 @@ impl 线段Py {
|
||||
_cls: &Bound<'_, PyType>,
|
||||
段: &Bound<'_, 虚线Py>,
|
||||
观察员: &Bound<'_, 观察者Py>,
|
||||
) -> Vec<虚线Py> {
|
||||
py: Python<'_>,
|
||||
) -> Vec<Py<虚线Py>> {
|
||||
let obs = 观察员.borrow();
|
||||
let obs_ref = obs.obs();
|
||||
chanlun::algorithm::segment::线段::获取所有停顿位置(&段.borrow().inner, &*obs_ref)
|
||||
chanlun::algorithm::segment::线段::获取所有停顿位置(&段.borrow().inner, &obs_ref)
|
||||
.into_iter()
|
||||
.map(|d| 虚线Py { inner: Arc::new(d) })
|
||||
.map(|d| dashed_to_py(py, Arc::new(d)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -1082,7 +1079,7 @@ impl 线段Py {
|
||||
) -> Vec<Py<缠论K线Py>> {
|
||||
let obs = 观察员.borrow();
|
||||
let obs_ref = obs.obs();
|
||||
chanlun::algorithm::segment::线段::是否背驰过(&当前段.borrow().inner, &*obs_ref)
|
||||
chanlun::algorithm::segment::线段::是否背驰过(&当前段.borrow().inner, &obs_ref)
|
||||
.into_iter()
|
||||
.map(|ck| chan_kline_to_py(py, ck))
|
||||
.collect()
|
||||
@@ -1264,7 +1261,7 @@ impl 中枢Py {
|
||||
fn 获取序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for d in self.inner.获取序列() {
|
||||
list.append(虚线Py { inner: d })?;
|
||||
list.append(dashed_to_py(py, d))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -1381,16 +1378,15 @@ impl 中枢Py {
|
||||
右: &Bound<'_, 虚线Py>,
|
||||
级别: i64,
|
||||
标识: &str,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(chanlun::algorithm::hub::中枢::创建(
|
||||
Arc::clone(&左.borrow().inner),
|
||||
Arc::clone(&中.borrow().inner),
|
||||
Arc::clone(&右.borrow().inner),
|
||||
级别,
|
||||
标识,
|
||||
)),
|
||||
}
|
||||
) -> Py<中枢Py> {
|
||||
let inner = Arc::new(chanlun::algorithm::hub::中枢::创建(
|
||||
Arc::clone(&左.borrow().inner),
|
||||
Arc::clone(&中.borrow().inner),
|
||||
Arc::clone(&右.borrow().inner),
|
||||
级别,
|
||||
标识,
|
||||
));
|
||||
hub_to_py(_cls.py(), inner)
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
@@ -1401,7 +1397,7 @@ impl 中枢Py {
|
||||
起始方向: &Bound<'_, 相对方向Py>,
|
||||
标识: &str,
|
||||
py: Python<'_>,
|
||||
) -> Option<Self> {
|
||||
) -> Option<Py<中枢Py>> {
|
||||
let rc_list: Vec<Arc<chanlun::structure::dash_line::虚线>> = 虚线序列
|
||||
.iter()
|
||||
.map(|d| Arc::clone(&d.bind(py).borrow().inner))
|
||||
@@ -1411,7 +1407,7 @@ impl 中枢Py {
|
||||
起始方向.borrow().inner,
|
||||
标识,
|
||||
)
|
||||
.map(|inner| Self { inner })
|
||||
.map(|inner| hub_to_py(py, inner))
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
@@ -1421,8 +1417,9 @@ impl 中枢Py {
|
||||
中枢序列: &Bound<'_, PyAny>,
|
||||
待添加中枢: &Bound<'_, Self>,
|
||||
) -> PyResult<()> {
|
||||
let py = 中枢序列.py();
|
||||
let inner = Arc::clone(&待添加中枢.borrow().inner);
|
||||
let wrapper = Py::new(中枢序列.py(), Self { inner })?;
|
||||
let wrapper = hub_to_py(py, inner);
|
||||
中枢序列.call_method1("append", (wrapper,))?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -1432,7 +1429,7 @@ impl 中枢Py {
|
||||
fn 从中枢序列尾部弹出(
|
||||
_cls: &Bound<'_, PyType>,
|
||||
中枢序列: &Bound<'_, PyAny>,
|
||||
待弹出中枢: &Bound<'_, Self>,
|
||||
_待弹出中枢: &Bound<'_, Self>,
|
||||
) -> PyResult<Option<Self>> {
|
||||
let result = 中枢序列.call_method1("pop", ())?;
|
||||
if result.is_none() {
|
||||
|
||||
@@ -28,14 +28,12 @@ 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};
|
||||
use crate::structure_py::{dashed_to_py, fractal_to_py, 分型Py};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
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;
|
||||
|
||||
// ========== 基础买卖点 ==========
|
||||
@@ -100,10 +98,8 @@ impl 基础买卖点Py {
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 买卖点分型(&self) -> 分型Py {
|
||||
分型Py {
|
||||
inner: Arc::clone(&self.inner.买卖点分型),
|
||||
}
|
||||
fn 买卖点分型(&self, py: Python<'_>) -> Py<分型Py> {
|
||||
fractal_to_py(py, Arc::clone(&self.inner.买卖点分型))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -560,18 +556,37 @@ impl 观察者Py {
|
||||
self.obs_mut().增加原始K线((*普K.borrow().inner).clone());
|
||||
}
|
||||
|
||||
/// 投喂原始数据 — 便捷入口,直接从 OHLCV 创建 K线 并投喂
|
||||
/// 投喂原始数据 — 便捷入口,直接从 OHLCV 创建 K线 并通过 Python 分发 增加原始K线,
|
||||
/// 确保子类重写的 增加原始K线 被正确调用。
|
||||
fn 投喂原始数据(
|
||||
&mut self, 时间戳: i64, 开: f64, 高: f64, 低: f64, 收: f64, 量: f64
|
||||
) {
|
||||
self.obs_mut().投喂原始数据(时间戳, 开, 高, 低, 收, 量);
|
||||
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<()> {
|
||||
// 重置基础序列
|
||||
slf.borrow_mut().obs_mut().重置基础序列();
|
||||
// 重置基础序列(通过 Python 分发,支持子类重写)
|
||||
slf.call_method1("重置基础序列", ())?;
|
||||
|
||||
// 读取文件,通过 Python dispatch 逐根投喂(支持子类重写 增加原始K线)
|
||||
let data = std::fs::read(文件路径)
|
||||
@@ -841,11 +856,11 @@ impl K线合成器Py {
|
||||
&mut self,
|
||||
普K: &Bound<'_, K线Py>,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<Vec<(i64, K线Py)>> {
|
||||
) -> PyResult<Vec<(i64, Py<K线Py>)>> {
|
||||
let results = self.inner.投喂K线((*普K.borrow().inner).clone());
|
||||
Ok(results
|
||||
.into_iter()
|
||||
.map(|(周期, k)| (周期, K线Py { inner: Arc::new(k) }))
|
||||
.map(|(周期, k)| (周期, bar_to_py(py, Arc::new(k))))
|
||||
.collect())
|
||||
}
|
||||
|
||||
@@ -858,7 +873,8 @@ impl K线合成器Py {
|
||||
低: f64,
|
||||
收: f64,
|
||||
量: f64,
|
||||
) -> Vec<(i64, K线Py)> {
|
||||
py: Python<'_>,
|
||||
) -> Vec<(i64, Py<K线Py>)> {
|
||||
let min_cycle = self.inner.周期组.iter().copied().min().unwrap_or(1);
|
||||
let k = chanlun::kline::bar::K线::创建普K(
|
||||
&self.inner.标识,
|
||||
@@ -874,22 +890,15 @@ impl K线合成器Py {
|
||||
let results = self.inner.投喂K线(k);
|
||||
results
|
||||
.into_iter()
|
||||
.map(|(周期, k2)| {
|
||||
(
|
||||
周期,
|
||||
K线Py {
|
||||
inner: Arc::new(k2),
|
||||
},
|
||||
)
|
||||
})
|
||||
.map(|(周期, k2)| (周期, bar_to_py(py, Arc::new(k2))))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// 获取指定周期当前正在合成的K线
|
||||
fn 获取当前K线(&self, 周期: i64) -> Option<K线Py> {
|
||||
self.inner.获取当前K线(周期).map(|k| K线Py {
|
||||
inner: Arc::new(k.clone()),
|
||||
})
|
||||
fn 获取当前K线(&self, 周期: i64, py: Python<'_>) -> Option<Py<K线Py>> {
|
||||
self.inner
|
||||
.获取当前K线(周期)
|
||||
.map(|k| bar_to_py(py, Arc::new(k.clone())))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
@@ -938,7 +947,7 @@ impl 立体分析器Py {
|
||||
};
|
||||
let cfg_map: Option<HashMap<i64, chanlun::config::缠论配置>> = match 配置组 {
|
||||
Some(dict_any) => {
|
||||
let dict = dict_any.downcast::<pyo3::types::PyDict>()?;
|
||||
let dict = dict_any.cast::<pyo3::types::PyDict>()?;
|
||||
let mut map = HashMap::new();
|
||||
for (key, value) in dict.iter() {
|
||||
let period: i64 = key.extract()?;
|
||||
|
||||
@@ -239,7 +239,7 @@ impl 缠论配置Py {
|
||||
) -> PyResult<Py<PyDict>> {
|
||||
let py = 原始字典.py();
|
||||
let result = PyDict::new(py);
|
||||
if let Ok(default_dict) = 默认配置.downcast::<PyDict>() {
|
||||
if let Ok(default_dict) = 默认配置.cast::<PyDict>() {
|
||||
for (key, value) in default_dict.iter() {
|
||||
if 原始字典.contains(&key)? {
|
||||
result.set_item(key.clone(), 原始字典.get_item(&key)?)?;
|
||||
@@ -252,6 +252,7 @@ impl 缠论配置Py {
|
||||
}
|
||||
|
||||
/// 比较当前配置与另一个配置的差异
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn 对比(
|
||||
&self,
|
||||
py: Python<'_>,
|
||||
@@ -290,7 +291,10 @@ impl 缠论配置Py {
|
||||
Ok(Self { fields })
|
||||
}
|
||||
|
||||
pub(crate) fn to_rust_config(&self, py: Python<'_>) -> PyResult<chanlun::config::缠论配置> {
|
||||
pub(crate) fn to_rust_config(
|
||||
&self,
|
||||
_py: Python<'_>,
|
||||
) -> PyResult<chanlun::config::缠论配置> {
|
||||
dict_to_rust_config(&self.fields)
|
||||
}
|
||||
|
||||
|
||||
+52
-38
@@ -347,26 +347,26 @@ impl K线Py {
|
||||
#[pyclass(name = "缠论K线", module = "chanlun._chanlun", from_py_object)]
|
||||
pub struct 缠论K线Py {
|
||||
pub(crate) inner: std::sync::Arc<chanlun::kline::chan_kline::缠论K线>,
|
||||
bsp_set: std::sync::RwLock<Option<Py<pyo3::types::PySet>>>,
|
||||
}
|
||||
|
||||
impl 缠论K线Py {
|
||||
pub(crate) fn from_rc(inner: std::sync::Arc<chanlun::kline::chan_kline::缠论K线>) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
bsp_set: std::sync::RwLock::new(None),
|
||||
}
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
/// 对象标识缓存:Rc 地址 → 规范 Python 对象
|
||||
/// 确保同一底层 Rc 指针在 Python 侧始终映射到同一 PyObject
|
||||
/// 对象标识缓存: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 BAR_IDENTITY: RwLock<HashMap<usize, Py<K线Py>>> = RwLock::new(HashMap::new());
|
||||
static KLINE_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<缠论K线Py>>>> =
|
||||
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||
|
||||
static KLINE_IDENTITY: RwLock<HashMap<usize, Py<缠论K线Py>>> = RwLock::new(HashMap::new());
|
||||
}
|
||||
/// 买卖点信息缓存 — 按 Arc 指针全局共享,确保所有 wrapper 看到同一 PySet
|
||||
static BSP_CACHE: std::sync::LazyLock<RwLock<HashMap<usize, Py<pyo3::types::PySet>>>> =
|
||||
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||
|
||||
/// 将 Rc<K线> 转为 Py<K线Py>,确保同一 Rc 地址总是返回同一 Python 对象
|
||||
pub(crate) fn bar_to_py(
|
||||
@@ -374,15 +374,16 @@ pub(crate) fn bar_to_py(
|
||||
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.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
||||
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.with(|c| {
|
||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
||||
});
|
||||
BAR_IDENTITY.write().unwrap().insert(key, obj.clone_ref(py));
|
||||
obj
|
||||
}
|
||||
|
||||
@@ -392,15 +393,19 @@ pub(crate) fn chan_kline_to_py(
|
||||
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.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
||||
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.with(|c| {
|
||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
||||
});
|
||||
KLINE_IDENTITY
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(key, obj.clone_ref(py));
|
||||
obj
|
||||
}
|
||||
|
||||
@@ -408,7 +413,6 @@ impl Clone for 缠论K线Py {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: std::sync::Arc::clone(&self.inner),
|
||||
bsp_set: std::sync::RwLock::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -530,16 +534,24 @@ impl 缠论K线Py {
|
||||
#[getter]
|
||||
/// 创建当前缠K的浅拷贝副本
|
||||
fn 镜像(&self, py: Python<'_>) -> Self {
|
||||
let mut mirror = Self {
|
||||
let mirror = Self {
|
||||
inner: std::sync::Arc::new(self.inner.镜像()),
|
||||
bsp_set: std::sync::RwLock::new(None),
|
||||
};
|
||||
if let Some(ref src_set) = *self.bsp_set.read().unwrap() {
|
||||
// 复制买卖点信息到镜像
|
||||
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 src_set.bind(py).iter() {
|
||||
for item in cached_src.bind(py).iter() {
|
||||
let _ = new_set.add(item);
|
||||
}
|
||||
mirror.bsp_set = std::sync::RwLock::new(Some(new_set.into()));
|
||||
let py_set: Py<pyo3::types::PySet> = new_set.into();
|
||||
BSP_CACHE.write().unwrap().insert(dst_key, py_set);
|
||||
}
|
||||
}
|
||||
mirror
|
||||
@@ -565,18 +577,20 @@ impl 缠论K线Py {
|
||||
|
||||
#[getter]
|
||||
fn 买卖点信息(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
if self.bsp_set.read().unwrap().is_none() {
|
||||
let set = pyo3::types::PySet::empty(py)?;
|
||||
for s in self.inner.买卖点信息.read().unwrap().iter() {
|
||||
set.add(s.clone())?;
|
||||
}
|
||||
*self.bsp_set.write().unwrap() = Some(set.into());
|
||||
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());
|
||||
}
|
||||
Ok(self
|
||||
.bsp_set
|
||||
// 创建新的 PySet 并存入全局缓存
|
||||
let set = pyo3::types::PySet::empty(py)?;
|
||||
BSP_CACHE.write().unwrap().insert(key, set.into());
|
||||
// 重新读取并返回(无法从 insert 获取 Py 引用,需要重新读)
|
||||
Ok(BSP_CACHE
|
||||
.read()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.get(&key)
|
||||
.unwrap()
|
||||
.clone_ref(py)
|
||||
.into_any())
|
||||
@@ -637,13 +651,13 @@ impl 缠论K线Py {
|
||||
配置: &Bound<'_, 缠论配置Py>,
|
||||
py: Python<'_>,
|
||||
) -> PyResult<(Option<Py<Self>>, Option<String>)> {
|
||||
let mut ck_inner = (*当前缠K.borrow().inner).clone();
|
||||
let ck_inner = (*当前缠K.borrow().inner).clone();
|
||||
let config = 配置.borrow().to_rust_config(py)?;
|
||||
let 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,
|
||||
&ck_inner,
|
||||
&当前普K.borrow().inner,
|
||||
&config,
|
||||
);
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#![allow(non_snake_case, clippy::too_many_arguments)]
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
|
||||
@@ -29,37 +29,45 @@ use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use crate::algorithm_py::{hub_to_py, 中枢Py};
|
||||
use crate::algorithm_py::hub_to_py;
|
||||
use crate::config_py::缠论配置Py;
|
||||
use crate::kline_py::{缠论K线Py, K线Py};
|
||||
use crate::kline_py::{bar_to_py, 缠论K线Py, K线Py};
|
||||
|
||||
// ---- 身份缓存 (弱引用:通过 refcnt 检测存活,仅缓存持有则视为过期) ----
|
||||
|
||||
thread_local! {
|
||||
static FRACTAL_IDENTITY: RwLock<HashMap<usize, Py<分型Py>>> = RwLock::new(HashMap::new());
|
||||
static DASHED_IDENTITY: RwLock<HashMap<usize, Py<虚线Py>>> = RwLock::new(HashMap::new());
|
||||
static SEGFEAT_IDENTITY: RwLock<HashMap<usize, Py<线段特征Py>>> = RwLock::new(HashMap::new());
|
||||
static FEATFRAC_IDENTITY: RwLock<HashMap<usize, Py<特征分型Py>>> = RwLock::new(HashMap::new());
|
||||
}
|
||||
// 使用全局 static 而非 thread_local!,保证跨线程对象标识一致性
|
||||
static FRACTAL_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<分型Py>>>> =
|
||||
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||
static DASHED_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<虚线Py>>>> =
|
||||
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||
static SEGFEAT_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<线段特征Py>>>> =
|
||||
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||
static FEATFRAC_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<特征分型Py>>>> =
|
||||
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||
|
||||
pub(crate) fn fractal_to_py(
|
||||
py: Python<'_>,
|
||||
inner: Arc<chanlun::structure::fractal_obj::分型>,
|
||||
) -> Py<分型Py> {
|
||||
let key = Arc::as_ptr(&inner) as usize;
|
||||
if let Some(cached) =
|
||||
FRACTAL_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
||||
if let Some(cached) = FRACTAL_IDENTITY
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&key)
|
||||
.map(|p| p.clone_ref(py))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
// 清理 refcnt==1 的过期条目(仅缓存持有,Python 侧已无引用)
|
||||
FRACTAL_IDENTITY.with(|c| {
|
||||
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
|
||||
});
|
||||
FRACTAL_IDENTITY
|
||||
.write()
|
||||
.unwrap()
|
||||
.retain(|_, v| v.get_refcnt(py) > 1);
|
||||
let obj = Py::new(py, 分型Py { inner }).unwrap();
|
||||
FRACTAL_IDENTITY.with(|c| {
|
||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
||||
});
|
||||
FRACTAL_IDENTITY
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(key, obj.clone_ref(py));
|
||||
obj
|
||||
}
|
||||
|
||||
@@ -68,18 +76,23 @@ pub(crate) fn dashed_to_py(
|
||||
inner: Arc<chanlun::structure::dash_line::虚线>,
|
||||
) -> Py<虚线Py> {
|
||||
let key = Arc::as_ptr(&inner) as usize;
|
||||
if let Some(cached) =
|
||||
DASHED_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
||||
if let Some(cached) = DASHED_IDENTITY
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&key)
|
||||
.map(|p| p.clone_ref(py))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
DASHED_IDENTITY.with(|c| {
|
||||
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
|
||||
});
|
||||
DASHED_IDENTITY
|
||||
.write()
|
||||
.unwrap()
|
||||
.retain(|_, v| v.get_refcnt(py) > 1);
|
||||
let obj = Py::new(py, 虚线Py { inner }).unwrap();
|
||||
DASHED_IDENTITY.with(|c| {
|
||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
||||
});
|
||||
DASHED_IDENTITY
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(key, obj.clone_ref(py));
|
||||
obj
|
||||
}
|
||||
|
||||
@@ -88,18 +101,23 @@ pub(crate) fn segfeat_to_py(
|
||||
inner: Arc<chanlun::structure::segment_feat::线段特征>,
|
||||
) -> Py<线段特征Py> {
|
||||
let key = Arc::as_ptr(&inner) as usize;
|
||||
if let Some(cached) =
|
||||
SEGFEAT_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
||||
if let Some(cached) = SEGFEAT_IDENTITY
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&key)
|
||||
.map(|p| p.clone_ref(py))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
SEGFEAT_IDENTITY.with(|c| {
|
||||
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
|
||||
});
|
||||
SEGFEAT_IDENTITY
|
||||
.write()
|
||||
.unwrap()
|
||||
.retain(|_, v| v.get_refcnt(py) > 1);
|
||||
let obj = Py::new(py, 线段特征Py { inner }).unwrap();
|
||||
SEGFEAT_IDENTITY.with(|c| {
|
||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
||||
});
|
||||
SEGFEAT_IDENTITY
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(key, obj.clone_ref(py));
|
||||
obj
|
||||
}
|
||||
|
||||
@@ -108,18 +126,23 @@ pub(crate) fn featfrac_to_py(
|
||||
inner: Arc<chanlun::structure::feat_fractal::特征分型>,
|
||||
) -> Py<特征分型Py> {
|
||||
let key = Arc::as_ptr(&inner) as usize;
|
||||
if let Some(cached) =
|
||||
FEATFRAC_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py)))
|
||||
if let Some(cached) = FEATFRAC_IDENTITY
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&key)
|
||||
.map(|p| p.clone_ref(py))
|
||||
{
|
||||
return cached;
|
||||
}
|
||||
FEATFRAC_IDENTITY.with(|c| {
|
||||
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1);
|
||||
});
|
||||
FEATFRAC_IDENTITY
|
||||
.write()
|
||||
.unwrap()
|
||||
.retain(|_, v| v.get_refcnt(py) > 1);
|
||||
let obj = Py::new(py, 特征分型Py { inner }).unwrap();
|
||||
FEATFRAC_IDENTITY.with(|c| {
|
||||
c.write().unwrap().insert(key, obj.clone_ref(py));
|
||||
});
|
||||
FEATFRAC_IDENTITY
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(key, obj.clone_ref(py));
|
||||
obj
|
||||
}
|
||||
use crate::types_py::{分型结构Py, 相对方向Py, 缺口Py};
|
||||
@@ -469,15 +492,13 @@ impl 虚线Py {
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn 前一结束位置(&self) -> Option<Self> {
|
||||
fn 前一结束位置(&self, py: Python<'_>) -> Option<Py<虚线Py>> {
|
||||
self.inner
|
||||
.前一结束位置
|
||||
.read()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.map(|d| Self {
|
||||
inner: Arc::clone(d),
|
||||
})
|
||||
.map(|d| dashed_to_py(py, Arc::clone(d)))
|
||||
}
|
||||
|
||||
// ---- 序列 getters ----
|
||||
@@ -486,12 +507,7 @@ impl 虚线Py {
|
||||
fn 基础序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for d in self.inner.基础序列.read().unwrap().iter() {
|
||||
list.append(Py::new(
|
||||
py,
|
||||
Self {
|
||||
inner: Arc::clone(d),
|
||||
},
|
||||
)?)?;
|
||||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -530,12 +546,7 @@ impl 虚线Py {
|
||||
fn 笔序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for d in self.inner.基础序列.read().unwrap().iter() {
|
||||
list.append(Py::new(
|
||||
py,
|
||||
Self {
|
||||
inner: Arc::clone(d),
|
||||
},
|
||||
)?)?;
|
||||
list.append(dashed_to_py(py, Arc::clone(d)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -582,11 +593,10 @@ impl 虚线Py {
|
||||
let obs_ref = 观察员.borrow();
|
||||
let observer_inner = obs_ref.obs();
|
||||
let result = self.inner.获取普K序列(&observer_inner.普通K线序列);
|
||||
let list = pyo3::types::PyList::empty(观察员.py());
|
||||
let py = 观察员.py();
|
||||
let list = pyo3::types::PyList::empty(py);
|
||||
for k in &result {
|
||||
list.append(K线Py {
|
||||
inner: Arc::clone(k),
|
||||
})?;
|
||||
list.append(bar_to_py(py, Arc::clone(k)))?;
|
||||
}
|
||||
Ok(list.into())
|
||||
}
|
||||
@@ -973,7 +983,7 @@ impl 虚线Py {
|
||||
) -> (bool, String) {
|
||||
let obs = 观察员.borrow();
|
||||
let obs_ref = obs.obs();
|
||||
chanlun::structure::dash_line::虚线::买卖意义(&实线.borrow().inner, &*obs_ref)
|
||||
chanlun::structure::dash_line::虚线::买卖意义(&实线.borrow().inner, &obs_ref)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1148,7 +1158,7 @@ impl 线段特征Py {
|
||||
let inner = Arc::make_mut(&mut self.inner);
|
||||
inner
|
||||
.添加(Arc::clone(&待添加虚线.borrow().inner))
|
||||
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e))
|
||||
.map_err(pyo3::exceptions::PyValueError::new_err)
|
||||
}
|
||||
|
||||
/// :param 待删除虚线: 待删除的虚线
|
||||
@@ -1156,7 +1166,7 @@ impl 线段特征Py {
|
||||
let inner = Arc::make_mut(&mut self.inner);
|
||||
inner
|
||||
.删除(&Arc::clone(&待删除虚线.borrow().inner))
|
||||
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e))
|
||||
.map_err(pyo3::exceptions::PyValueError::new_err)
|
||||
}
|
||||
|
||||
// ---- classmethods ----
|
||||
|
||||
@@ -545,7 +545,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
// 买卖点类型 class attributes (singleton instances)
|
||||
let py = m.py();
|
||||
let bsp_class = m.getattr("买卖点类型")?;
|
||||
let bsp_class = bsp_class.downcast_into::<PyType>()?;
|
||||
let bsp_class = bsp_class.cast_into::<PyType>()?;
|
||||
|
||||
let variants: &[(&str, chanlun::types::买卖点类型)] = &[
|
||||
("一买", chanlun::types::买卖点类型::一买),
|
||||
@@ -568,7 +568,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
("T3B卖", chanlun::types::买卖点类型::T3B卖),
|
||||
];
|
||||
|
||||
let mut bsp_members = PyDict::new(py);
|
||||
let bsp_members = PyDict::new(py);
|
||||
for (name, value) in variants {
|
||||
let instance = Py::new(py, 买卖点类型Py { inner: *value })?;
|
||||
bsp_class.setattr(*name, instance.clone_ref(py))?;
|
||||
@@ -577,7 +577,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
bsp_class.setattr("__members__", bsp_members)?;
|
||||
|
||||
// 相对方向 class attributes
|
||||
let dir_class = m.getattr("相对方向")?.downcast_into::<PyType>()?.clone();
|
||||
let dir_class = m.getattr("相对方向")?.cast_into::<PyType>()?.clone();
|
||||
let dir_variants: &[(&str, chanlun::types::相对方向)] = &[
|
||||
("向上", chanlun::types::相对方向::向上),
|
||||
("向下", chanlun::types::相对方向::向下),
|
||||
@@ -590,7 +590,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
("同", chanlun::types::相对方向::同),
|
||||
];
|
||||
|
||||
let mut dir_members = PyDict::new(py);
|
||||
let dir_members = PyDict::new(py);
|
||||
for (name, value) in dir_variants {
|
||||
let instance = Py::new(py, 相对方向Py { inner: *value })?;
|
||||
dir_class.setattr(*name, instance.clone_ref(py))?;
|
||||
@@ -599,7 +599,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
dir_class.setattr("__members__", dir_members)?;
|
||||
|
||||
// 分型结构 class attributes
|
||||
let frac_class = m.getattr("分型结构")?.downcast_into::<PyType>()?.clone();
|
||||
let frac_class = m.getattr("分型结构")?.cast_into::<PyType>()?.clone();
|
||||
let frac_variants: &[(&str, chanlun::types::分型结构)] = &[
|
||||
("上", chanlun::types::分型结构::上),
|
||||
("下", chanlun::types::分型结构::下),
|
||||
@@ -608,7 +608,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
("散", chanlun::types::分型结构::散),
|
||||
];
|
||||
|
||||
let mut frac_members = PyDict::new(py);
|
||||
let frac_members = PyDict::new(py);
|
||||
for (name, value) in frac_variants {
|
||||
let instance = Py::new(py, 分型结构Py { inner: *value })?;
|
||||
frac_class.setattr(*name, instance.clone_ref(py))?;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "chanlun"
|
||||
version = "26.5.5"
|
||||
version = "26.5.6"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
license = "MIT"
|
||||
|
||||
+15
-24
@@ -78,16 +78,18 @@ impl 笔 {
|
||||
- 高_k.标的K线.read().unwrap().序号)
|
||||
.unsigned_abs() as usize;
|
||||
// 向上笔
|
||||
if 筆.方向().是否向上() && 低_k.低.get() < 筆.低() {
|
||||
if 原始数量 >= 配置.笔弱化_原始数量 as usize {
|
||||
return 配置.笔内元素数量 as usize;
|
||||
}
|
||||
if 筆.方向().是否向上()
|
||||
&& 低_k.低.get() < 筆.低()
|
||||
&& 原始数量 >= 配置.笔弱化_原始数量 as usize
|
||||
{
|
||||
return 配置.笔内元素数量 as usize;
|
||||
}
|
||||
// 向下笔
|
||||
if 筆.方向().是否向下() && 低_k.低.get() > 筆.高() {
|
||||
if 原始数量 >= 配置.笔弱化_原始数量 as usize {
|
||||
return 配置.笔内元素数量 as usize;
|
||||
}
|
||||
if 筆.方向().是否向下()
|
||||
&& 低_k.低.get() > 筆.高()
|
||||
&& 原始数量 >= 配置.笔弱化_原始数量 as usize
|
||||
{
|
||||
return 配置.笔内元素数量 as usize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -960,22 +962,11 @@ impl 笔 {
|
||||
for i in 3..基础序列.len() - 1 {
|
||||
let k = &基础序列[i];
|
||||
|
||||
if *k.分型.read().unwrap() == Some(分型结构::顶) && 筆.方向() == 相对方向::向上
|
||||
{
|
||||
let 左 = Arc::clone(&基础序列[i - 1]);
|
||||
let 中 = Arc::clone(k);
|
||||
let 右 = Arc::clone(&基础序列[i + 1]);
|
||||
let 武 = 分型::new(Some(左), 中, Some(右));
|
||||
let 当前笔 = 虚线::创建笔(Arc::clone(&文), Arc::new(武), true);
|
||||
当前笔
|
||||
.序号
|
||||
.store(筆.序号.load(Ordering::Relaxed), Ordering::Relaxed);
|
||||
if Self::自检(&当前笔, 观察员) {
|
||||
笔序列.push(当前笔);
|
||||
}
|
||||
} else if *k.分型.read().unwrap() == Some(分型结构::底)
|
||||
&& 筆.方向() == 相对方向::向下
|
||||
{
|
||||
let 匹配顶 =
|
||||
*k.分型.read().unwrap() == Some(分型结构::顶) && 筆.方向() == 相对方向::向上;
|
||||
let 匹配底 =
|
||||
*k.分型.read().unwrap() == Some(分型结构::底) && 筆.方向() == 相对方向::向下;
|
||||
if 匹配顶 || 匹配底 {
|
||||
let 左 = Arc::clone(&基础序列[i - 1]);
|
||||
let 中 = Arc::clone(k);
|
||||
let 右 = Arc::clone(&基础序列[i + 1]);
|
||||
|
||||
@@ -39,13 +39,13 @@ impl 背驰分析 {
|
||||
) -> bool {
|
||||
let 进入MACD = Self::_获取MACD面积(
|
||||
K线序列,
|
||||
&*进入段.文.中.标的K线.read().unwrap(),
|
||||
&*进入段.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
&进入段.文.中.标的K线.read().unwrap(),
|
||||
&进入段.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
);
|
||||
let 离开MACD = Self::_获取MACD面积(
|
||||
K线序列,
|
||||
&*离开段.文.中.标的K线.read().unwrap(),
|
||||
&*离开段.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
&离开段.文.中.标的K线.read().unwrap(),
|
||||
&离开段.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
);
|
||||
|
||||
// 计算面积(绝对值求和)
|
||||
|
||||
@@ -415,7 +415,7 @@ impl 中枢 {
|
||||
中枢序列: &mut Vec<Arc<中枢>>,
|
||||
待弹出: &Arc<中枢>,
|
||||
) -> Option<Arc<中枢>> {
|
||||
if 中枢序列.last().map(|x| Arc::as_ptr(x)) == Some(Arc::as_ptr(待弹出)) {
|
||||
if 中枢序列.last().map(Arc::as_ptr) == Some(Arc::as_ptr(待弹出)) {
|
||||
中枢序列.pop()
|
||||
} else {
|
||||
None
|
||||
@@ -430,7 +430,7 @@ impl 中枢 {
|
||||
中枢序列: &mut Vec<Arc<中枢>>,
|
||||
跳过首部: bool,
|
||||
标识: &str,
|
||||
层级: i64,
|
||||
_层级: i64,
|
||||
) {
|
||||
if 虚线序列.len() < 3 {
|
||||
return;
|
||||
@@ -472,7 +472,7 @@ impl 中枢 {
|
||||
));
|
||||
Self::向中枢序列尾部添加(中枢序列, 新中枢);
|
||||
// Python: return 中枢递归分析(虚线序列, 中枢序列, ...)
|
||||
Self::分析(虚线序列, 中枢序列, 跳过首部, 标识, 层级);
|
||||
Self::分析(虚线序列, 中枢序列, 跳过首部, 标识, _层级);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -487,7 +487,7 @@ impl 中枢 {
|
||||
if needs_pop {
|
||||
let 当前中枢 = Arc::clone(&中枢序列[当前中枢_idx]);
|
||||
Self::从中枢序列尾部弹出(中枢序列, &当前中枢);
|
||||
Self::分析(虚线序列, 中枢序列, 跳过首部, 标识, 层级);
|
||||
Self::分析(虚线序列, 中枢序列, 跳过首部, 标识, _层级);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -508,8 +508,8 @@ impl 中枢 {
|
||||
let mut 中枢低 = 中枢序列[当前中枢_idx].低();
|
||||
let mut 候选序列: Vec<Arc<虚线>> = Vec::new();
|
||||
|
||||
for i in 起始索引..虚线序列.len() {
|
||||
let 当前虚线 = Arc::clone(&虚线序列[i]);
|
||||
for 当前虚线_ref in &虚线序列[起始索引..] {
|
||||
let 当前虚线 = Arc::clone(当前虚线_ref);
|
||||
|
||||
// 检查是否超出中枢范围(缺口)
|
||||
if crate::types::相对方向::分析(中枢高, 中枢低, 当前虚线.高(), 当前虚线.低()).是否缺口()
|
||||
@@ -566,6 +566,30 @@ impl 中枢 {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for 中枢 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let 序列_str = self
|
||||
.基础序列
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|d| format!("{}", d))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
write!(
|
||||
f,
|
||||
"{}({}, {}, 元素数量: {}, [{}], {} ===>>> {})",
|
||||
self.标识.read().unwrap(),
|
||||
crate::utils::format_f64_g(self.高()),
|
||||
crate::utils::format_f64_g(self.低()),
|
||||
self.基础序列.read().unwrap().len(),
|
||||
序列_str,
|
||||
self.文(),
|
||||
self.武(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -575,13 +599,14 @@ mod tests {
|
||||
use crate::types::分型结构;
|
||||
|
||||
fn 辅助_创建K线(时间戳: i64, 高: f64, 低: f64, 开: f64, 收: f64) -> K线 {
|
||||
let mut k = K线::default();
|
||||
k.时间戳 = 时间戳;
|
||||
k.高 = 高;
|
||||
k.低 = 低;
|
||||
k.开盘价 = 开;
|
||||
k.收盘价 = 收;
|
||||
k
|
||||
K线 {
|
||||
时间戳,
|
||||
高,
|
||||
低,
|
||||
开盘价: 开,
|
||||
收盘价: 收,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn 辅助_创建缠K(
|
||||
@@ -710,7 +735,7 @@ mod tests {
|
||||
中枢.设置第三买卖线(Arc::clone(&笔1));
|
||||
assert!(中枢.第三买卖线.read().unwrap().is_some());
|
||||
assert_eq!(
|
||||
Arc::as_ptr(&*中枢.第三买卖线.read().unwrap().as_ref().unwrap()),
|
||||
Arc::as_ptr(中枢.第三买卖线.read().unwrap().as_ref().unwrap()),
|
||||
Arc::as_ptr(&笔1)
|
||||
);
|
||||
|
||||
@@ -870,27 +895,3 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for 中枢 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let 序列_str = self
|
||||
.基础序列
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|d| format!("{}", d))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
write!(
|
||||
f,
|
||||
"{}({}, {}, 元素数量: {}, [{}], {} ===>>> {})",
|
||||
self.标识.read().unwrap(),
|
||||
crate::utils::format_f64_g(self.高()),
|
||||
crate::utils::format_f64_g(self.低()),
|
||||
self.基础序列.read().unwrap().len(),
|
||||
序列_str,
|
||||
self.文(),
|
||||
self.武(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,14 @@ use std::sync::Arc;
|
||||
/// 线段 — 从笔生成线段的算法集合(静态方法命名空间)
|
||||
pub struct 线段;
|
||||
|
||||
type 分割结果 = (
|
||||
Vec<Arc<虚线>>,
|
||||
Vec<Arc<虚线>>,
|
||||
Vec<Arc<虚线>>,
|
||||
Option<Arc<虚线>>,
|
||||
);
|
||||
type 中枢序列组 = (Vec<Arc<中枢>>, Vec<Arc<中枢>>, Vec<Arc<中枢>>);
|
||||
|
||||
impl 线段 {
|
||||
// ================================================================
|
||||
// 基础操作
|
||||
@@ -53,8 +61,7 @@ impl 线段 {
|
||||
let 段 = Self::取段(段_rc);
|
||||
if !段.基础序列.read().unwrap().is_empty() {
|
||||
if !分型::判断分型(
|
||||
&*段
|
||||
.基础序列
|
||||
&段.基础序列
|
||||
.read()
|
||||
.unwrap()
|
||||
.last()
|
||||
@@ -182,11 +189,9 @@ impl 线段 {
|
||||
if !序列.iter().any(|x| Arc::as_ptr(x) == Arc::as_ptr(元素)) {
|
||||
break;
|
||||
}
|
||||
if !基础序列.is_empty() {
|
||||
if !基础序列.last().unwrap().之后是(元素) {
|
||||
eprintln!("线段._验证序列 数据不连续");
|
||||
break;
|
||||
}
|
||||
if !基础序列.is_empty() && !基础序列.last().unwrap().之后是(元素) {
|
||||
eprintln!("线段._验证序列 数据不连续");
|
||||
break;
|
||||
}
|
||||
基础序列.push(Arc::clone(元素));
|
||||
}
|
||||
@@ -204,10 +209,8 @@ impl 线段 {
|
||||
if !序列.iter().any(|x| Arc::as_ptr(x) == Arc::as_ptr(元素)) {
|
||||
break;
|
||||
}
|
||||
if !基础序列.is_empty() {
|
||||
if !基础序列.last().unwrap().之后是(元素) {
|
||||
break;
|
||||
}
|
||||
if !基础序列.is_empty() && !基础序列.last().unwrap().之后是(元素) {
|
||||
break;
|
||||
}
|
||||
基础序列.push(Arc::clone(元素));
|
||||
}
|
||||
@@ -301,7 +304,7 @@ impl 线段 {
|
||||
/// 特征分型终结 — 检查特征序列是否形成正常分型终结
|
||||
pub fn 特征分型终结(段: &虚线) -> bool {
|
||||
let 特征序列 = 线段特征::静态分析(
|
||||
&*段.基础序列.read().unwrap(),
|
||||
&段.基础序列.read().unwrap(),
|
||||
段.方向(),
|
||||
&Self::四象(段),
|
||||
false,
|
||||
@@ -347,11 +350,9 @@ impl 线段 {
|
||||
return;
|
||||
}
|
||||
|
||||
for 特征 in &序列 {
|
||||
if let Some(ref f) = 特征 {
|
||||
if f.方向() == 段.方向() {
|
||||
panic!("特征序列方向不匹配[{}]", 行号);
|
||||
}
|
||||
for f in 序列.iter().flatten() {
|
||||
if f.方向() == 段.方向() {
|
||||
panic!("特征序列方向不匹配[{}]", 行号);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,15 +473,7 @@ impl 线段 {
|
||||
// ================================================================
|
||||
|
||||
/// 分割序列 — 将线段的基础序列分为前、后、第三买卖线、贯穿伤
|
||||
pub fn 分割序列(
|
||||
段: &虚线,
|
||||
所属中枢: Option<&中枢>,
|
||||
) -> (
|
||||
Vec<Arc<虚线>>,
|
||||
Vec<Arc<虚线>>,
|
||||
Vec<Arc<虚线>>,
|
||||
Option<Arc<虚线>>,
|
||||
) {
|
||||
pub fn 分割序列(段: &虚线, 所属中枢: Option<&中枢>) -> 分割结果 {
|
||||
if 段.模式.read().unwrap().as_str() != "文武" {
|
||||
return (
|
||||
段.基础序列.read().unwrap().clone(),
|
||||
@@ -517,9 +510,9 @@ impl 线段 {
|
||||
|
||||
let mut 状态 = None;
|
||||
|
||||
if let Some(ref 中枢) = 所属中枢 {
|
||||
if let Some(中枢) = 所属中枢 {
|
||||
*中枢.本级_第三买卖线.write().unwrap() = None;
|
||||
let 尾部 = if let Some(ref 后笔) = 后.last() {
|
||||
let 尾部 = if let Some(后笔) = 后.last() {
|
||||
后笔.武.read().unwrap().clone()
|
||||
} else {
|
||||
段.武.read().unwrap().clone()
|
||||
@@ -572,7 +565,7 @@ impl 线段 {
|
||||
|
||||
if !第三买卖线.is_empty() {
|
||||
第三买卖线.reverse();
|
||||
if let Some(ref 中枢) = 所属中枢 {
|
||||
if let Some(中枢) = 所属中枢 {
|
||||
*中枢.本级_第三买卖线.write().unwrap() = Some(Arc::clone(&第三买卖线[0]));
|
||||
}
|
||||
}
|
||||
@@ -626,13 +619,11 @@ impl 线段 {
|
||||
}) == Some(true)
|
||||
{
|
||||
Some(Arc::clone(最近特征.元素.last().unwrap()))
|
||||
} else if let Some(b) = 笔::以武会友(
|
||||
&*段2.基础序列.read().unwrap(),
|
||||
&*最近特征.元素.last().unwrap().武.read().unwrap(),
|
||||
) {
|
||||
Some(b)
|
||||
} else {
|
||||
None
|
||||
笔::以武会友(
|
||||
&段2.基础序列.read().unwrap(),
|
||||
&最近特征.元素.last().unwrap().武.read().unwrap(),
|
||||
)
|
||||
};
|
||||
|
||||
if 特征后一笔.is_none() {
|
||||
@@ -686,14 +677,11 @@ impl 线段 {
|
||||
}
|
||||
|
||||
/// 获取内部中枢序列 — 内部实现
|
||||
fn 获取内部中枢序列_内部(
|
||||
段: &虚线,
|
||||
_配置: &缠论配置,
|
||||
) -> (Vec<Arc<中枢>>, Vec<Arc<中枢>>, Vec<Arc<中枢>>) {
|
||||
fn 获取内部中枢序列_内部(段: &虚线, _配置: &缠论配置) -> 中枢序列组 {
|
||||
if 段.模式.read().unwrap().as_str() != "文武" {
|
||||
中枢::分析(
|
||||
&*段.基础序列.read().unwrap(),
|
||||
&mut *段.合_中枢序列.write().unwrap(),
|
||||
&段.基础序列.read().unwrap(),
|
||||
&mut 段.合_中枢序列.write().unwrap(),
|
||||
true,
|
||||
&format!(
|
||||
"{}_{}_合_",
|
||||
@@ -714,7 +702,7 @@ impl 线段 {
|
||||
|
||||
中枢::分析(
|
||||
&前,
|
||||
&mut *段.实_中枢序列.write().unwrap(),
|
||||
&mut 段.实_中枢序列.write().unwrap(),
|
||||
true,
|
||||
&format!(
|
||||
"{}_{}_实_",
|
||||
@@ -725,7 +713,7 @@ impl 线段 {
|
||||
);
|
||||
中枢::分析(
|
||||
&后,
|
||||
&mut *段.虚_中枢序列.write().unwrap(),
|
||||
&mut 段.虚_中枢序列.write().unwrap(),
|
||||
true,
|
||||
&format!(
|
||||
"{}_{}_虚_",
|
||||
@@ -735,8 +723,8 @@ impl 线段 {
|
||||
0,
|
||||
);
|
||||
中枢::分析(
|
||||
&*段.基础序列.read().unwrap(),
|
||||
&mut *段.合_中枢序列.write().unwrap(),
|
||||
&段.基础序列.read().unwrap(),
|
||||
&mut 段.合_中枢序列.write().unwrap(),
|
||||
true,
|
||||
&format!(
|
||||
"{}_{}_合_",
|
||||
@@ -755,9 +743,8 @@ impl 线段 {
|
||||
|
||||
/// 获取内部中枢序列
|
||||
pub fn 获取内部中枢序列(
|
||||
段_rc: &Arc<虚线>,
|
||||
配置: &缠论配置,
|
||||
) -> (Vec<Arc<中枢>>, Vec<Arc<中枢>>, Vec<Arc<中枢>>) {
|
||||
段_rc: &Arc<虚线>, 配置: &缠论配置
|
||||
) -> 中枢序列组 {
|
||||
let 段 = Self::取段(段_rc);
|
||||
Self::获取内部中枢序列_内部(段, 配置)
|
||||
}
|
||||
@@ -942,7 +929,7 @@ impl 线段 {
|
||||
*cur.基础序列.write().unwrap() = 当前线段基础序列;
|
||||
}
|
||||
let idx = 线段序列.len() - 1;
|
||||
Self::刷新(&mut 线段序列[idx], 配置);
|
||||
Self::刷新(&线段序列[idx], 配置);
|
||||
true
|
||||
}
|
||||
|
||||
@@ -1038,7 +1025,7 @@ impl 线段 {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(_) = 开始序号_opt {
|
||||
if 开始序号_opt.is_some() {
|
||||
let idx = 线段序列.len() - 1;
|
||||
let seg_rc = Arc::clone(&线段序列[idx]);
|
||||
for 临时虚线 in &待添加元素 {
|
||||
@@ -1047,7 +1034,7 @@ impl 线段 {
|
||||
线段序列[idx] = seg_rc;
|
||||
}
|
||||
let idx = 线段序列.len() - 1;
|
||||
Self::刷新(&mut 线段序列[idx], 配置);
|
||||
Self::刷新(&线段序列[idx], 配置);
|
||||
|
||||
let 当前线段 = Arc::clone(&线段序列[idx]);
|
||||
if 当前线段.特征序列.read().unwrap().len() >= 3
|
||||
@@ -1310,8 +1297,8 @@ impl 线段 {
|
||||
}
|
||||
|
||||
// ---- 3. 确保当前线段有效 ----
|
||||
let mut 当前线段_rc = Arc::clone(线段序列.last().unwrap());
|
||||
Self::序列重置(&mut 当前线段_rc, 笔序列);
|
||||
let 当前线段_rc = Arc::clone(线段序列.last().unwrap());
|
||||
Self::序列重置(&当前线段_rc, 笔序列);
|
||||
let seg_idx = 线段序列.len() - 1;
|
||||
线段序列[seg_idx] = 当前线段_rc;
|
||||
|
||||
@@ -1347,7 +1334,7 @@ impl 线段 {
|
||||
|
||||
// Refresh current segment
|
||||
let idx = 线段序列.len() - 1;
|
||||
Self::刷新(&mut 线段序列[idx], 配置);
|
||||
Self::刷新(&线段序列[idx], 配置);
|
||||
|
||||
// ---- 5. 调用一次全局修正 ----
|
||||
Self::_缺口突破(线段序列, 配置, 当前层级);
|
||||
@@ -1375,20 +1362,20 @@ impl 线段 {
|
||||
|
||||
let mut 需要递归 = false;
|
||||
|
||||
for i in 起始索引..笔序列.len() {
|
||||
let 当前虚线 = Arc::clone(&笔序列[i]);
|
||||
for 当前虚线_ref in &笔序列[起始索引..] {
|
||||
let 当前虚线 = Arc::clone(当前虚线_ref);
|
||||
let 当前线段 = Arc::clone(线段序列.last().unwrap());
|
||||
let 四象 = Self::四象(&当前线段);
|
||||
|
||||
// 向当前线段添加笔
|
||||
let mut 线段_rc = Arc::clone(线段序列.last().unwrap());
|
||||
Self::添加虚线(&mut 线段_rc, Arc::clone(&当前虚线));
|
||||
let 线段_rc = Arc::clone(线段序列.last().unwrap());
|
||||
Self::添加虚线(&线段_rc, Arc::clone(&当前虚线));
|
||||
let seg_idx = 线段序列.len() - 1;
|
||||
线段序列[seg_idx] = 线段_rc;
|
||||
|
||||
// 刷新
|
||||
let idx = 线段序列.len() - 1;
|
||||
Self::刷新(&mut 线段序列[idx], 配置);
|
||||
Self::刷新(&线段序列[idx], 配置);
|
||||
|
||||
// 依次尝试四种修正
|
||||
if Self::_缺口突破(线段序列, 配置, 当前层级) {
|
||||
@@ -1444,14 +1431,14 @@ impl 线段 {
|
||||
break;
|
||||
}
|
||||
// 向新段添加当前虚线
|
||||
let mut 新段_rc = Arc::clone(线段序列.last().unwrap());
|
||||
Self::添加虚线(&mut 新段_rc, Arc::clone(&当前虚线));
|
||||
let 新段_rc = Arc::clone(线段序列.last().unwrap());
|
||||
Self::添加虚线(&新段_rc, Arc::clone(&当前虚线));
|
||||
let seg_idx = 线段序列.len() - 1;
|
||||
线段序列[seg_idx] = 新段_rc;
|
||||
}
|
||||
|
||||
let idx = 线段序列.len() - 1;
|
||||
Self::刷新(&mut 线段序列[idx], 配置);
|
||||
Self::刷新(&线段序列[idx], 配置);
|
||||
}
|
||||
|
||||
if 需要递归 {
|
||||
@@ -1572,8 +1559,8 @@ impl 线段 {
|
||||
}
|
||||
|
||||
// 验证当前线段
|
||||
let mut 当前线段_rc = Arc::clone(线段序列.last().unwrap());
|
||||
Self::验证序列(&mut 当前线段_rc, 虚线序列);
|
||||
let 当前线段_rc = Arc::clone(线段序列.last().unwrap());
|
||||
Self::验证序列(&当前线段_rc, 虚线序列);
|
||||
let seg_idx = 线段序列.len() - 1;
|
||||
线段序列[seg_idx] = 当前线段_rc;
|
||||
|
||||
@@ -1597,7 +1584,7 @@ impl 线段 {
|
||||
*cur.基础序列.write().unwrap() = 前三个;
|
||||
}
|
||||
let seg_idx = 线段序列.len() - 1;
|
||||
Self::武终(&mut 线段序列[seg_idx], 0);
|
||||
Self::武终(&线段序列[seg_idx], 0);
|
||||
} else {
|
||||
let 当前 = Arc::clone(线段序列.last().unwrap());
|
||||
Self::_弹出扩展线段(线段序列, &当前, line!());
|
||||
@@ -1608,7 +1595,7 @@ impl 线段 {
|
||||
|
||||
// 武终
|
||||
let idx = 线段序列.len() - 1;
|
||||
Self::武终(&mut 线段序列[idx], 0);
|
||||
Self::武终(&线段序列[idx], 0);
|
||||
|
||||
let 当前线段 = Arc::clone(线段序列.last().unwrap());
|
||||
if 当前线段
|
||||
@@ -1647,18 +1634,18 @@ impl 线段 {
|
||||
let 相对关系 = 相对方向::分析(左.高(), 左.低(), 右.高(), 右.低());
|
||||
|
||||
if 相对关系.是否缺口() {
|
||||
let mut 段_rc = Arc::clone(线段序列.last().unwrap());
|
||||
Self::添加虚线(&mut 段_rc, Arc::clone(左));
|
||||
let 段_rc = Arc::clone(线段序列.last().unwrap());
|
||||
Self::添加虚线(&段_rc, Arc::clone(左));
|
||||
let seg_idx = 线段序列.len() - 1;
|
||||
线段序列[seg_idx] = 段_rc;
|
||||
|
||||
let mut 段_rc = Arc::clone(线段序列.last().unwrap());
|
||||
Self::添加虚线(&mut 段_rc, Arc::clone(中));
|
||||
let 段_rc = Arc::clone(线段序列.last().unwrap());
|
||||
Self::添加虚线(&段_rc, Arc::clone(中));
|
||||
let seg_idx = 线段序列.len() - 1;
|
||||
线段序列[seg_idx] = 段_rc;
|
||||
|
||||
let seg_idx = 线段序列.len() - 1;
|
||||
Self::武终(&mut 线段序列[seg_idx], 0);
|
||||
Self::武终(&线段序列[seg_idx], 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1730,8 +1717,8 @@ impl 线段 {
|
||||
{
|
||||
let k线序列 = K线::截取rc(
|
||||
&观察员.普通K线序列,
|
||||
&*阳[阳.len() - 3].文.中.标的K线.read().unwrap(),
|
||||
&*阳[阳.len() - 1]
|
||||
&阳[阳.len() - 3].文.中.标的K线.read().unwrap(),
|
||||
&阳[阳.len() - 1]
|
||||
.武
|
||||
.read()
|
||||
.unwrap()
|
||||
@@ -1851,15 +1838,13 @@ impl 线段 {
|
||||
for 筆 in &阳 {
|
||||
if 笔序列.len() >= 3 {
|
||||
let 筆停顿 = 笔::获取所有停顿位置(筆, 观察员);
|
||||
let mut 停顿列表: Vec<Arc<虚线>> =
|
||||
筆停顿.into_iter().map(|b| Arc::new(b)).collect();
|
||||
let mut 停顿列表: Vec<Arc<虚线>> = 筆停顿.into_iter().map(Arc::new).collect();
|
||||
停顿列表.push(Arc::clone(筆));
|
||||
|
||||
for 停顿 in &停顿列表 {
|
||||
笔序列.push(Arc::clone(停顿));
|
||||
线段序列.clear();
|
||||
let 笔序列_slice: Vec<Arc<虚线>> =
|
||||
笔序列.iter().map(|b| Arc::clone(b)).collect();
|
||||
let 笔序列_slice: Vec<Arc<虚线>> = 笔序列.iter().map(Arc::clone).collect();
|
||||
Self::分析(
|
||||
&笔序列_slice,
|
||||
&mut 线段序列,
|
||||
@@ -1881,13 +1866,12 @@ impl 线段 {
|
||||
if !重复 {
|
||||
if let Some(最后线段) = 线段序列.last() {
|
||||
if 最后线段.基础序列.read().unwrap().len() % 2 == 1 {
|
||||
let 新段 =
|
||||
虚线::创建线段(&*最后线段.基础序列.read().unwrap());
|
||||
let 新段 = 虚线::创建线段(&最后线段.基础序列.read().unwrap());
|
||||
新段
|
||||
.序号
|
||||
.store(段.序号.load(Ordering::Relaxed), Ordering::Relaxed);
|
||||
let mut 新段_rc = Arc::new(新段);
|
||||
Self::刷新(&mut 新段_rc, &观察员.配置);
|
||||
let 新段_rc = Arc::new(新段);
|
||||
Self::刷新(&新段_rc, &观察员.配置);
|
||||
let 新段_inner =
|
||||
Arc::try_unwrap(新段_rc).unwrap_or_else(|rc| (*rc).clone());
|
||||
if 新段_inner.方向() == 段.方向() {
|
||||
@@ -1919,8 +1903,8 @@ impl 线段 {
|
||||
let mut 结果 = Vec::new();
|
||||
|
||||
for 段 in 停顿位置 {
|
||||
let mut 段_rc = Arc::new(段);
|
||||
Self::获取内部中枢序列(&mut 段_rc, &观察员.配置);
|
||||
let 段_rc = Arc::new(段);
|
||||
Self::获取内部中枢序列(&段_rc, &观察员.配置);
|
||||
if Self::判断线段内部是否背驰(&段_rc, 观察员) {
|
||||
结果.push(Arc::clone(&段_rc.武.read().unwrap().中));
|
||||
}
|
||||
|
||||
@@ -590,7 +590,7 @@ mod tests {
|
||||
let config = 缠论配置::default();
|
||||
let obs_ref = 观察者::new("btcusd".into(), 300, config);
|
||||
|
||||
let data = std::fs::read(&test_data_path()).unwrap();
|
||||
let data = std::fs::read(test_data_path()).unwrap();
|
||||
let size = 48;
|
||||
|
||||
for i in 0..data.len() / size {
|
||||
@@ -720,7 +720,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_重复计算后结果一致() {
|
||||
let data = std::fs::read(&test_data_path()).unwrap();
|
||||
let data = std::fs::read(test_data_path()).unwrap();
|
||||
let size = 48;
|
||||
|
||||
let 计算 = || {
|
||||
@@ -764,7 +764,7 @@ mod tests {
|
||||
let config = 缠论配置::default();
|
||||
let obs_ref = 观察者::new("btcusd".into(), 300, config);
|
||||
|
||||
let data = std::fs::read(&test_data_path()).unwrap();
|
||||
let data = std::fs::read(test_data_path()).unwrap();
|
||||
let size = 48;
|
||||
|
||||
for i in 0..data.len() / size {
|
||||
|
||||
@@ -77,6 +77,7 @@ impl Default for 随机指标 {
|
||||
|
||||
impl 随机指标 {
|
||||
/// 首次计算 KDJ(无历史数据时)
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn 首次计算(
|
||||
初始最高价: f64,
|
||||
初始最低价: f64,
|
||||
|
||||
@@ -141,6 +141,7 @@ impl K线 {
|
||||
}
|
||||
|
||||
/// 创建普通K线
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn 创建普K(
|
||||
标识: &str,
|
||||
时间戳: i64,
|
||||
|
||||
@@ -199,19 +199,16 @@ impl 缠论K线 {
|
||||
if k线.时间戳.load(Ordering::Relaxed) <= k.时间戳.load(Ordering::Relaxed)
|
||||
&& k.时间戳.load(Ordering::Relaxed)
|
||||
<= k线.时间戳.load(Ordering::Relaxed) + k线.周期
|
||||
&& (k线.分型特征值.get() - k.分型特征值.get()).abs() < f64::EPSILON
|
||||
{
|
||||
if (k线.分型特征值.get() - k.分型特征值.get()).abs() < f64::EPSILON
|
||||
{
|
||||
return k.时间戳.load(Ordering::Relaxed);
|
||||
}
|
||||
return k.时间戳.load(Ordering::Relaxed);
|
||||
}
|
||||
} else if k.时间戳.load(Ordering::Relaxed) <= k线.时间戳.load(Ordering::Relaxed)
|
||||
&& k线.时间戳.load(Ordering::Relaxed)
|
||||
<= k.时间戳.load(Ordering::Relaxed) + k.周期
|
||||
&& (k线.分型特征值.get() - k.分型特征值.get()).abs() < f64::EPSILON
|
||||
{
|
||||
if (k线.分型特征值.get() - k.分型特征值.get()).abs() < f64::EPSILON {
|
||||
return k.时间戳.load(Ordering::Relaxed);
|
||||
}
|
||||
return k.时间戳.load(Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,6 +216,7 @@ impl 缠论K线 {
|
||||
}
|
||||
|
||||
/// 创建缠K
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn 创建缠K(
|
||||
时间戳: i64,
|
||||
高: f64,
|
||||
@@ -420,43 +418,41 @@ impl 缠论K线 {
|
||||
if 之前普K.时间戳 == 当前K线.时间戳 {
|
||||
// 同时间戳更新
|
||||
当前K线.序号 = 之前普K.序号;
|
||||
if 配置.计算指标 {
|
||||
if 普K序列.len() >= 2 {
|
||||
if let Some(ref prev_macd) = 普K序列[普K序列.len() - 2].macd {
|
||||
当前K线.macd = Some(平滑异同移动平均线::增量计算(
|
||||
prev_macd,
|
||||
K线取值(
|
||||
当前K线.开盘价,
|
||||
当前K线.高,
|
||||
当前K线.低,
|
||||
当前K线.收盘价,
|
||||
&配置.指标计算方式,
|
||||
),
|
||||
当前K线.时间戳,
|
||||
));
|
||||
}
|
||||
if let Some(ref prev_rsi) = 普K序列[普K序列.len() - 2].rsi {
|
||||
当前K线.rsi = Some(相对强弱指数::增量计算(
|
||||
prev_rsi,
|
||||
K线取值(
|
||||
当前K线.开盘价,
|
||||
当前K线.高,
|
||||
当前K线.低,
|
||||
当前K线.收盘价,
|
||||
&配置.指标计算方式,
|
||||
),
|
||||
当前K线.时间戳,
|
||||
));
|
||||
}
|
||||
if let Some(ref prev_kdj) = 普K序列[普K序列.len() - 2].kdj {
|
||||
当前K线.kdj = Some(随机指标::增量计算(
|
||||
prev_kdj,
|
||||
if 配置.计算指标 && 普K序列.len() >= 2 {
|
||||
if let Some(ref prev_macd) = 普K序列[普K序列.len() - 2].macd {
|
||||
当前K线.macd = Some(平滑异同移动平均线::增量计算(
|
||||
prev_macd,
|
||||
K线取值(
|
||||
当前K线.开盘价,
|
||||
当前K线.高,
|
||||
当前K线.低,
|
||||
当前K线.收盘价,
|
||||
当前K线.时间戳,
|
||||
));
|
||||
}
|
||||
&配置.指标计算方式,
|
||||
),
|
||||
当前K线.时间戳,
|
||||
));
|
||||
}
|
||||
if let Some(ref prev_rsi) = 普K序列[普K序列.len() - 2].rsi {
|
||||
当前K线.rsi = Some(相对强弱指数::增量计算(
|
||||
prev_rsi,
|
||||
K线取值(
|
||||
当前K线.开盘价,
|
||||
当前K线.高,
|
||||
当前K线.低,
|
||||
当前K线.收盘价,
|
||||
&配置.指标计算方式,
|
||||
),
|
||||
当前K线.时间戳,
|
||||
));
|
||||
}
|
||||
if let Some(ref prev_kdj) = 普K序列[普K序列.len() - 2].kdj {
|
||||
当前K线.kdj = Some(随机指标::增量计算(
|
||||
prev_kdj,
|
||||
当前K线.高,
|
||||
当前K线.低,
|
||||
当前K线.收盘价,
|
||||
当前K线.时间戳,
|
||||
));
|
||||
}
|
||||
}
|
||||
普K序列.pop();
|
||||
@@ -514,7 +510,7 @@ impl 缠论K线 {
|
||||
if !缠K序列.is_empty() {
|
||||
let len = 缠K序列.len();
|
||||
let (左边, 右边) = 缠K序列.split_at_mut(len - 1);
|
||||
let 之前缠K: Option<&缠论K线> = 左边.last().map(|rc| Arc::as_ref(rc));
|
||||
let 之前缠K: Option<&缠论K线> = 左边.last().map(Arc::as_ref);
|
||||
let 最后一个缠K = &*右边[0];
|
||||
let (新缠K, 模式) = Self::兼并(之前缠K, 最后一个缠K, 当前K线_ref, 配置);
|
||||
|
||||
@@ -614,7 +610,7 @@ impl 缠论K线 {
|
||||
Arc::clone(&缠K序列[idx - 2]),
|
||||
Some(Arc::clone(&缠K序列[idx - 1])),
|
||||
));
|
||||
return (状态, Some(形态));
|
||||
(状态, Some(形态))
|
||||
}
|
||||
|
||||
/// 截取缠K序列从始到终
|
||||
@@ -623,12 +619,8 @@ impl 缠论K线 {
|
||||
始: &缠论K线,
|
||||
终: &缠论K线,
|
||||
) -> Option<Vec<Arc<缠论K线>>> {
|
||||
let 始_idx = 序列
|
||||
.iter()
|
||||
.position(|k| Arc::as_ptr(k) == (始 as *const _))?;
|
||||
let 终_idx = 序列
|
||||
.iter()
|
||||
.position(|k| Arc::as_ptr(k) == (终 as *const _))?;
|
||||
let 始_idx = 序列.iter().position(|k| std::ptr::eq(Arc::as_ptr(k), 始))?;
|
||||
let 终_idx = 序列.iter().position(|k| std::ptr::eq(Arc::as_ptr(k), 终))?;
|
||||
Some(序列[始_idx..=终_idx].to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,8 +435,8 @@ impl 虚线 {
|
||||
pub fn 计算MACD柱子均值(普K序列: &[Arc<K线>], 实线: &虚线) -> f64 {
|
||||
let K线序列 = K线::截取rc(
|
||||
普K序列,
|
||||
&*实线.文.中.标的K线.read().unwrap(),
|
||||
&*实线.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
&实线.文.中.标的K线.read().unwrap(),
|
||||
&实线.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
);
|
||||
if K线序列.is_empty() {
|
||||
return 0.0;
|
||||
@@ -453,8 +453,8 @@ impl 虚线 {
|
||||
pub fn 计算MACD柱子均值_阴(普K序列: &[Arc<K线>], 实线: &虚线) -> Option<f64> {
|
||||
let K线序列 = K线::截取rc(
|
||||
普K序列,
|
||||
&*实线.文.中.标的K线.read().unwrap(),
|
||||
&*实线.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
&实线.文.中.标的K线.read().unwrap(),
|
||||
&实线.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
);
|
||||
let 总: Vec<f64> = K线序列
|
||||
.iter()
|
||||
@@ -473,8 +473,8 @@ impl 虚线 {
|
||||
pub fn 计算MACD柱子均值_阳(普K序列: &[Arc<K线>], 实线: &虚线) -> Option<f64> {
|
||||
let K线序列 = K线::截取rc(
|
||||
普K序列,
|
||||
&*实线.文.中.标的K线.read().unwrap(),
|
||||
&*实线.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
&实线.文.中.标的K线.read().unwrap(),
|
||||
&实线.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
);
|
||||
let 总: Vec<f64> = K线序列
|
||||
.iter()
|
||||
@@ -549,8 +549,8 @@ impl 虚线 {
|
||||
};
|
||||
let K线序列 = K线::截取rc(
|
||||
普K序列,
|
||||
&*实线.文.中.标的K线.read().unwrap(),
|
||||
&*实线.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
&实线.文.中.标的K线.read().unwrap(),
|
||||
&实线.武.read().unwrap().中.标的K线.read().unwrap(),
|
||||
);
|
||||
let 所有柱子: Vec<f64> = K线序列
|
||||
.iter()
|
||||
@@ -583,7 +583,7 @@ impl 虚线 {
|
||||
if 方向 == 相对方向::向上 {
|
||||
let 柱子序列: Vec<&Arc<K线>> = 普K序列
|
||||
.iter()
|
||||
.filter(|k| k.macd.as_ref().map_or(false, |m| m.MACD柱 > 0.0))
|
||||
.filter(|k| k.macd.as_ref().is_some_and(|m| m.MACD柱 > 0.0))
|
||||
.collect();
|
||||
if 柱子序列.is_empty() {
|
||||
return [false, false, false];
|
||||
@@ -603,7 +603,7 @@ impl 虚线 {
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
})
|
||||
.unwrap();
|
||||
let mut 柱对 = vec![Arc::clone(*最高柱子), Arc::clone(最后)];
|
||||
let mut 柱对 = [Arc::clone(*最高柱子), Arc::clone(最后)];
|
||||
柱对.sort_by_key(|k| k.时间戳);
|
||||
if let (Some(m0), Some(m1)) = (柱对[0].macd.as_ref(), 柱对[1].macd.as_ref()) {
|
||||
if m0.MACD柱 > m1.MACD柱 && 柱对[0].高 < 柱对[1].高 {
|
||||
@@ -649,7 +649,7 @@ impl 虚线 {
|
||||
} else {
|
||||
let 柱子序列: Vec<&Arc<K线>> = 普K序列
|
||||
.iter()
|
||||
.filter(|k| k.macd.as_ref().map_or(false, |m| m.MACD柱 < 0.0))
|
||||
.filter(|k| k.macd.as_ref().is_some_and(|m| m.MACD柱 < 0.0))
|
||||
.collect();
|
||||
if 柱子序列.is_empty() {
|
||||
return [false, false, false];
|
||||
@@ -670,7 +670,7 @@ impl 虚线 {
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
})
|
||||
.unwrap();
|
||||
let mut 柱对 = vec![Arc::clone(*最高柱子), Arc::clone(最后)];
|
||||
let mut 柱对 = [Arc::clone(*最高柱子), Arc::clone(最后)];
|
||||
柱对.sort_by_key(|k| k.时间戳);
|
||||
if let (Some(m0), Some(m1)) = (柱对[0].macd.as_ref(), 柱对[1].macd.as_ref()) {
|
||||
if m0.MACD柱 < m1.MACD柱 && 柱对[0].低 > 柱对[1].低 {
|
||||
@@ -947,16 +947,62 @@ impl 虚线 {
|
||||
}
|
||||
}
|
||||
|
||||
if !结果 && 意义 && 实线.武.read().unwrap().中.与MACD柱子匹配() {
|
||||
if Self::武之MACD极值(普K序列, 实线) && 背驰过.len() > 2 {
|
||||
return (true, "没结果, 极值, 柱子分型匹配, 背驰过大于2次".into());
|
||||
}
|
||||
if !结果
|
||||
&& 意义
|
||||
&& 实线.武.read().unwrap().中.与MACD柱子匹配()
|
||||
&& Self::武之MACD极值(普K序列, 实线)
|
||||
&& 背驰过.len() > 2
|
||||
{
|
||||
return (true, "没结果, 极值, 柱子分型匹配, 背驰过大于2次".into());
|
||||
}
|
||||
|
||||
(结果, "".into())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for 虚线 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if *self.标识.read().unwrap() == "笔" {
|
||||
write!(
|
||||
f,
|
||||
"笔({}, {}, {}, {}, 周期: {}, 数量: {})",
|
||||
self.序号.load(Ordering::Relaxed),
|
||||
self.方向(),
|
||||
self.文,
|
||||
self.武.read().unwrap(),
|
||||
self.文.中.周期,
|
||||
self.武.read().unwrap().中.序号.load(Ordering::Relaxed)
|
||||
- self.文.中.序号.load(Ordering::Relaxed)
|
||||
+ 1
|
||||
)
|
||||
} else {
|
||||
let 四象 = crate::algorithm::segment::线段::四象(self);
|
||||
let 缺口 = crate::algorithm::segment::线段::获取缺口(self);
|
||||
let 缺口_str = match 缺口 {
|
||||
Some(g) => format!("{}", g),
|
||||
None => "None".to_string(),
|
||||
};
|
||||
let 确认K线_str = match &*self.确认K线.read().unwrap() {
|
||||
Some(k) => format!("{}", k),
|
||||
None => "None".to_string(),
|
||||
};
|
||||
write!(
|
||||
f,
|
||||
"{}<{}, {}, {}, {}, {}, 数量: {}, 缺口: {}, {}>",
|
||||
self.标识.read().unwrap(),
|
||||
self.序号.load(Ordering::Relaxed),
|
||||
四象,
|
||||
self.方向(),
|
||||
self.文,
|
||||
self.武.read().unwrap(),
|
||||
self.基础序列.read().unwrap().len(),
|
||||
缺口_str,
|
||||
确认K线_str,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -966,13 +1012,14 @@ mod tests {
|
||||
|
||||
/// 辅助:创建一根最小化的原始K线
|
||||
fn 辅助_创建K线(时间戳: i64, 高: f64, 低: f64, 开: f64, 收: f64) -> K线 {
|
||||
let mut k = K线::default();
|
||||
k.时间戳 = 时间戳;
|
||||
k.高 = 高;
|
||||
k.低 = 低;
|
||||
k.开盘价 = 开;
|
||||
k.收盘价 = 收;
|
||||
k
|
||||
K线 {
|
||||
时间戳,
|
||||
高,
|
||||
低,
|
||||
开盘价: 开,
|
||||
收盘价: 收,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// 辅助:创建一根缠论K线
|
||||
@@ -1236,46 +1283,3 @@ mod tests {
|
||||
assert_eq!(新武耗时, 300);
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for 虚线 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if *self.标识.read().unwrap() == "笔" {
|
||||
write!(
|
||||
f,
|
||||
"笔({}, {}, {}, {}, 周期: {}, 数量: {})",
|
||||
self.序号.load(Ordering::Relaxed),
|
||||
self.方向(),
|
||||
self.文,
|
||||
self.武.read().unwrap(),
|
||||
self.文.中.周期,
|
||||
self.武.read().unwrap().中.序号.load(Ordering::Relaxed)
|
||||
- self.文.中.序号.load(Ordering::Relaxed)
|
||||
+ 1
|
||||
)
|
||||
} else {
|
||||
let 四象 = crate::algorithm::segment::线段::四象(self);
|
||||
let 缺口 = crate::algorithm::segment::线段::获取缺口(self);
|
||||
let 缺口_str = match 缺口 {
|
||||
Some(g) => format!("{}", g),
|
||||
None => "None".to_string(),
|
||||
};
|
||||
let 确认K线_str = match &*self.确认K线.read().unwrap() {
|
||||
Some(k) => format!("{}", k),
|
||||
None => "None".to_string(),
|
||||
};
|
||||
write!(
|
||||
f,
|
||||
"{}<{}, {}, {}, {}, {}, 数量: {}, 缺口: {}, {}>",
|
||||
self.标识.read().unwrap(),
|
||||
self.序号.load(Ordering::Relaxed),
|
||||
四象,
|
||||
self.方向(),
|
||||
self.文,
|
||||
self.武.read().unwrap(),
|
||||
self.基础序列.read().unwrap().len(),
|
||||
缺口_str,
|
||||
确认K线_str,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ impl 线段特征 {
|
||||
|
||||
/// 新建特征序列元素
|
||||
pub fn 新建(虚线序列: Vec<Arc<虚线>>, 线段方向: 相对方向) -> Self {
|
||||
let 标识 = format!("特征<虚线>");
|
||||
let 标识 = "特征<虚线>".to_string();
|
||||
Self::new(标识, 虚线序列, 线段方向)
|
||||
}
|
||||
|
||||
@@ -320,13 +320,14 @@ mod tests {
|
||||
use crate::types::分型结构;
|
||||
|
||||
fn 辅助_创建K线(时间戳: i64, 高: f64, 低: f64, 开: f64, 收: f64) -> K线 {
|
||||
let mut k = K线::default();
|
||||
k.时间戳 = 时间戳;
|
||||
k.高 = 高;
|
||||
k.低 = 低;
|
||||
k.开盘价 = 开;
|
||||
k.收盘价 = 收;
|
||||
k
|
||||
K线 {
|
||||
时间戳,
|
||||
高,
|
||||
低,
|
||||
开盘价: 开,
|
||||
收盘价: 收,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn 辅助_创建缠K(
|
||||
|
||||
@@ -92,6 +92,7 @@ impl 分型结构 {
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn 分析_内部(
|
||||
左高: f64,
|
||||
左低: f64,
|
||||
|
||||
@@ -42,7 +42,7 @@ pub fn format_f64_g(value: f64) -> String {
|
||||
let exp = abs.log10().floor() as i32;
|
||||
|
||||
// Python :g 科学计数法边界: exp < -4 或 exp >= p (=6)
|
||||
if exp < -4 || exp >= 6 {
|
||||
if !(-4..6).contains(&exp) {
|
||||
let significand = value / 10_f64.powi(exp);
|
||||
let s = format!("{:.5}", significand);
|
||||
let s = s.trim_end_matches('0').trim_end_matches('.');
|
||||
@@ -57,12 +57,12 @@ pub fn format_f64_g(value: f64) -> String {
|
||||
}
|
||||
let s = format!("{:.prec$}", value, prec = 6 - int_digits);
|
||||
let s = s.trim_end_matches('0');
|
||||
return s.trim_end_matches('.').to_string();
|
||||
s.trim_end_matches('.').to_string()
|
||||
} else {
|
||||
let leading_zeros = (-exp) as usize;
|
||||
let s = format!("{:.prec$}", value, prec = leading_zeros + 5);
|
||||
let s = s.trim_end_matches('0');
|
||||
return s.trim_end_matches('.').to_string();
|
||||
s.trim_end_matches('.').to_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+63
-34
@@ -657,35 +657,56 @@ class 随机数据(bt.feeds.DataBase):
|
||||
class 自定义实时数据源(bt.feed.DataBase):
|
||||
"""
|
||||
一个用于模拟实时数据推送的数据源,继承自Backtrader的DataBase。
|
||||
在实际应用中,你需要将“模拟生成数据”的部分,替换为接收WebSocket等推送数据的代码。
|
||||
|
||||
支持两种数据输入方式:
|
||||
1. 手动投喂:调用 投喂数据(时间戳, 开, 高, 低, 收, 量) 从 WebSocket 回调等外部代码推送
|
||||
2. 后台线程:传入 观察员 和 魔法 参数,start() 时自动启动线程获取历史数据
|
||||
(注意:观察员 需实现 读取任意数据 方法)
|
||||
|
||||
时间戳格式:Unix 时间戳(秒),为 int 类型。
|
||||
"""
|
||||
|
||||
def __init__(self, 数据队列: queue.Queue, 观察员: "观察者", 魔法, **魔法参数):
|
||||
# 调用父类初始化方法
|
||||
def __init__(self, 数据队列: queue.Queue = None, 观察员: "观察者" = None, 魔法=None, **魔法参数):
|
||||
super(自定义实时数据源, self).__init__()
|
||||
# 存储外部传入的数据队列,用于接收实时数据
|
||||
self.数据队列 = 数据队列
|
||||
# 定义一个标志,用于控制数据加载的停止
|
||||
self.数据队列 = 数据队列 or queue.Queue()
|
||||
self.正在运行 = False
|
||||
self.观察员 = 观察员
|
||||
self.魔法 = 魔法
|
||||
self.__魔法参数 = 魔法参数
|
||||
self.已有数据 = False
|
||||
|
||||
def 投喂数据(self, 时间戳: int, 开盘价: float, 最高价: float, 最低价: float, 收盘价: float, 成交量: float, 持仓量: float = 0):
|
||||
"""线程安全的外部数据推送接口,供 WebSocket 回调等外部代码调用。
|
||||
|
||||
:param 时间戳: Unix 时间戳(秒,int 类型)
|
||||
:param 开盘价: 开盘价
|
||||
:param 最高价: 最高价
|
||||
:param 最低价: 最低价
|
||||
:param 收盘价: 收盘价
|
||||
:param 成交量: 成交量
|
||||
:param 持仓量: 持仓量(默认为 0)
|
||||
"""
|
||||
self.数据队列.put((时间戳, 开盘价, 最高价, 最低价, 收盘价, 成交量, 持仓量))
|
||||
|
||||
def start(self):
|
||||
"""
|
||||
数据源开始工作时的初始化操作。
|
||||
这里可以不执行任何操作,或者启动数据接收线程等。
|
||||
"""
|
||||
print(f"[{datetime.now()}] 自定义数据源已启动...")
|
||||
self.正在运行 = True
|
||||
|
||||
def 运行回测():
|
||||
self.观察员.读取任意数据(self.魔法, **self.__魔法参数)
|
||||
self.正在运行 = False
|
||||
if self.观察员 is not None and self.魔法 is not None:
|
||||
if not hasattr(self.观察员, "读取任意数据"):
|
||||
print(f"[{datetime.now()}] 警告: 观察员没有 '读取任意数据' 方法,后台线程不会启动")
|
||||
return
|
||||
|
||||
回测线程 = threading.Thread(target=运行回测, daemon=True)
|
||||
回测线程.start()
|
||||
def 运行回测():
|
||||
try:
|
||||
self.观察员.读取任意数据(self.魔法, **self.__魔法参数)
|
||||
except Exception as e:
|
||||
print(f"[{datetime.now()}] 后台数据线程异常: {e}")
|
||||
finally:
|
||||
self.正在运行 = False
|
||||
|
||||
回测线程 = threading.Thread(target=运行回测, daemon=True)
|
||||
回测线程.start()
|
||||
|
||||
def stop(self):
|
||||
"""
|
||||
@@ -696,32 +717,25 @@ class 自定义实时数据源(bt.feed.DataBase):
|
||||
print(f"[{datetime.now()}] 自定义数据源已停止。")
|
||||
|
||||
def _load(self):
|
||||
"""
|
||||
(核心方法) Backtrader会循环调用此方法来获取数据。
|
||||
每次被调用时,都应该返回新的数据行(一个K线/一个数据点)。
|
||||
如果没有新数据,返回 False,Backtrader会等待下次调用。
|
||||
"""
|
||||
"""Backtrader 核心回调 — 阻塞等待数据,有数据时返回 True,数据源停止时返回 False。"""
|
||||
if not self.正在运行:
|
||||
return False
|
||||
# 当没有新数据且系统仍在运行时,进入等待
|
||||
# 从外部队列获取新数据
|
||||
|
||||
while True:
|
||||
try:
|
||||
data_point = self.数据队列.get(timeout=0.5)
|
||||
break
|
||||
except queue.Empty:
|
||||
if not self.已有数据:
|
||||
continue
|
||||
else:
|
||||
if self.正在运行:
|
||||
continue
|
||||
if not self.正在运行:
|
||||
return False
|
||||
# 解析数据元组
|
||||
dt, o, h, l, c, v, oi = data_point
|
||||
|
||||
# 将数据写入Backtrader的数据线 (lines)
|
||||
self.lines.datetime[0] = bt.date2num(dt)
|
||||
try:
|
||||
dt, o, h, l, c, v, oi = data_point
|
||||
except (ValueError, TypeError) as e:
|
||||
print(f"[{datetime.now()}] 自定义数据源: 数据格式错误,跳过: {e}")
|
||||
return True
|
||||
|
||||
self.lines.datetime[0] = bt.date2num(datetime.utcfromtimestamp(int(dt)))
|
||||
self.lines.open[0] = o
|
||||
self.lines.high[0] = h
|
||||
self.lines.low[0] = l
|
||||
@@ -729,7 +743,6 @@ class 自定义实时数据源(bt.feed.DataBase):
|
||||
self.lines.volume[0] = v
|
||||
self.lines.openinterest[0] = oi
|
||||
self.已有数据 = True
|
||||
# 返回 True 表示成功加载一行数据
|
||||
return True
|
||||
|
||||
|
||||
@@ -757,7 +770,7 @@ class 高级策略基类(bt.Strategy):
|
||||
|
||||
def 日志(self, 文本, 时间=None):
|
||||
时间 = 时间 or self.datas[0].datetime.datetime(0)
|
||||
print(f"{时间} {文本}")
|
||||
print(f"{时间} {self.p.观察员.__class__.__name__}: {文本}")
|
||||
|
||||
def 计算目标数量(self, 价格):
|
||||
"""根据资金类型和仓位比例计算目标数量"""
|
||||
@@ -946,6 +959,7 @@ class 回测(高级策略基类):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.已处理信号 = set()
|
||||
print(f"{self.p.观察员.__class__.__name__}: 加载完成...")
|
||||
|
||||
def 获取开仓限价(self, 是否做多):
|
||||
# 根据缠论分型计算限价,若无则返回 None 使用基类默认逻辑
|
||||
@@ -957,6 +971,7 @@ class 回测(高级策略基类):
|
||||
return None
|
||||
|
||||
def next(self):
|
||||
# self.日志(f"{self.p.观察员.__class__.__name__} called next ")
|
||||
# 1. 更新移动止损(基类方法)
|
||||
if self.position:
|
||||
self.更新止损订单(self.position.size > 0, self.data.close[0])
|
||||
@@ -986,15 +1001,22 @@ class 回测(高级策略基类):
|
||||
def 检查买信号(self):
|
||||
if self.p.观察员.笔序列:
|
||||
k线 = self.p.观察员.缠论K线序列[-1]
|
||||
self.日志(f"检查买信号 当前笔 {self.p.观察员.笔序列[-1]}")
|
||||
if k线.买卖点信息:
|
||||
print(f"回测-首 {self.p.观察员.__class__.__name__}", k线.买卖点信息)
|
||||
首 = True if k线.买卖点信息 and "买" in next(iter(k线.买卖点信息)) else False
|
||||
if 首:
|
||||
print(k线)
|
||||
原始差值 = self.p.观察员.当前K线.序号 - k线.标的K线.序号
|
||||
差值 = self.p.观察员.当前缠K.序号 - k线.序号
|
||||
self.日志(f"首_买入信号差值: {原始差值}, {差值}, 观察员.当前K线 时间戳: {self.p.观察员.当前K线.时间戳}")
|
||||
k线 = self.p.观察员.缠论K线序列[-2]
|
||||
if k线.买卖点信息:
|
||||
print(f"回测-尾 {self.p.观察员.__class__.__name__}", k线.买卖点信息)
|
||||
|
||||
尾 = True if self.p.观察员.缠论K线序列[-2].买卖点信息 and "买" in next(iter(self.p.观察员.缠论K线序列[-2].买卖点信息)) else False
|
||||
if 尾:
|
||||
print(k线)
|
||||
原始差值 = self.p.观察员.当前K线.序号 - k线.标的K线.序号
|
||||
差值 = self.p.观察员.当前缠K.序号 - k线.序号
|
||||
self.日志(f"尾_买入信号差值: {原始差值}, {差值}, 观察员.当前K线 时间戳: {self.p.观察员.当前K线.时间戳}")
|
||||
@@ -1002,16 +1024,23 @@ class 回测(高级策略基类):
|
||||
|
||||
def 检查卖信号(self):
|
||||
if self.p.观察员.笔序列:
|
||||
self.日志(f"检查卖信号 当前笔 {self.p.观察员.笔序列[-1]}")
|
||||
k线 = self.p.观察员.缠论K线序列[-1]
|
||||
if k线.买卖点信息:
|
||||
print(f"回测-首 {self.p.观察员.__class__.__name__}", k线.买卖点信息)
|
||||
首 = True if k线.买卖点信息 and "卖" in next(iter(k线.买卖点信息)) else False
|
||||
if 首:
|
||||
print(k线)
|
||||
原始差值 = self.p.观察员.当前K线.序号 - k线.标的K线.序号
|
||||
差值 = self.p.观察员.当前缠K.序号 - k线.序号
|
||||
self.日志(f"首_买入信号差值: {原始差值}, {差值}, 观察员.当前K线 时间戳: {self.p.观察员.当前K线.时间戳}")
|
||||
k线 = self.p.观察员.缠论K线序列[-2]
|
||||
if k线.买卖点信息:
|
||||
print(f"回测-尾 {self.p.观察员.__class__.__name__}", k线.买卖点信息)
|
||||
|
||||
尾 = True if self.p.观察员.缠论K线序列[-2].买卖点信息 and "卖" in next(iter(self.p.观察员.缠论K线序列[-2].买卖点信息)) else False
|
||||
if 尾:
|
||||
print(k线)
|
||||
原始差值 = self.p.观察员.当前K线.序号 - k线.标的K线.序号
|
||||
差值 = self.p.观察员.当前缠K.序号 - k线.序号
|
||||
self.日志(f"尾_买入信号差值: {原始差值}, {差值}, 观察员.当前K线 时间戳: {self.p.观察员.当前K线.时间戳}")
|
||||
@@ -1019,7 +1048,7 @@ class 回测(高级策略基类):
|
||||
|
||||
def log(self, 文本, dt=None):
|
||||
dt = dt or bt.num2date(self.data.datetime[0])
|
||||
print(f"[{dt.strftime('%Y-%m-%d %H:%M')}] {self.p.符号} | {文本}")
|
||||
print(f"[{dt.strftime('%Y-%m-%d %H:%M')}] {self.p.观察员.__class__.__name__}: {self.p.符号} | {文本}")
|
||||
|
||||
|
||||
# ==================== 回测运行入口 ====================
|
||||
|
||||
Reference in New Issue
Block a user