信号 版本一

This commit is contained in:
YuWuKunCheng
2026-06-27 18:17:57 +08:00
parent 8405d478bc
commit 16ed3de8f5
91 changed files with 20966 additions and 7712 deletions
+1
View File
@@ -0,0 +1 @@
/target/
+594
View File
@@ -0,0 +1,594 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "autocfg"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "bitflags"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.2.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chanlun"
version = "26.6.4"
dependencies = [
"byteorder",
"chanlun-signal-macros",
"chrono",
"fastrand",
"inventory",
"parking_lot",
"serde",
"serde_json",
"sha2",
"tracing",
]
[[package]]
name = "chanlun-signal-macros"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "chrono"
version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
dependencies = [
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-link",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "fastrand"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "futures-core"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-task"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
[[package]]
name = "futures-util"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "iana-time-zone"
version = "0.1.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "inventory"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b"
dependencies = [
"rustversion",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "js-sys"
version = "0.3.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31"
dependencies = [
"cfg-if",
"futures-util",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
[[package]]
name = "memchr"
version = "2.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "parking_lot"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-link",
]
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "plugin-demo"
version = "0.1.0"
dependencies = [
"chanlun",
"chanlun-signal-macros",
"inventory",
"parking_lot",
"serde_json",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags",
]
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "shlex"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "smallvec"
version = "1.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
[[package]]
name = "syn"
version = "2.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tracing"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
]
[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasm-bindgen"
version = "0.2.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f"
dependencies = [
"unicode-ident",
]
[[package]]
name = "windows-core"
version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-result"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
dependencies = [
"windows-link",
]
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "plugin-demo"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
chanlun = { path = "../../chanlun" }
chanlun-signal-macros = { path = "../../chanlun-signal-macros" }
inventory = "0.3"
serde_json = "1"
parking_lot = "0.12"
+202
View File
@@ -0,0 +1,202 @@
//! chanlun 信号插件示例 — 两种动态注册方式。
//!
//! 编译: `cargo build` → `target/debug/libplugin_demo.so`
//! 加载: Python → `ctypes.CDLL(...)` → `init_plugin_manual()` / `init_plugin_macro()`
//!
//! # 方式 A: 手动 C-ABI 注册
//! - 实现 `fn(SignalFn)` 信号函数
//! - `init` 中调用 `chanlun_register_signal(name, template, func_ptr)`
//!
//! # 方式 B: #[signal] 宏 + inventory 批量提交
//! - 用 `#[signal]` 宏写信号函数(与宿主内写法完全一致)
//! - `init` 中遍历 `inventory::iter::<SignalDescriptor>` 批量调用 `chanlun_register_signal`
use std::collections::HashMap;
use std::os::raw::c_char;
use chanlun::business::observer::;
use chanlun::signal::registry::SignalFn;
use chanlun::signal::Signal;
use serde_json::Value;
// ── C-ABI: 宿主导出的符号(由 dlopen 的动态链接器解析)──
unsafe extern "C" {
fn chanlun_register_signal(
name: *const c_char,
template: *const c_char,
func: SignalFn,
) -> i32;
fn chanlun_unregister_signal(name: *const c_char) -> i32;
}
// ═══════════════════════════════════════════════════════════
// 方式 A: 手动 C-ABI 注册
// ═══════════════════════════════════════════════════════════
/// 插件信号 AMACD 零上强势(DIF > 0 && DIF > DEA)。
///
/// 手动注册 — 不依赖 `#[signal]` 宏,不依赖 `chanlun-signal-macros`。
fn MACD强势_V999999(obs: &, params: &HashMap<String, Value>) -> Vec<Signal> {
obs.();
let freq = params.get("freq").and_then(|v| v.as_str()).unwrap_or("日线");
let di = params.get("di").and_then(|v| v.as_i64()).unwrap_or(1) as usize;
let k1 = freq.to_string();
let k2 = format!("D{di}");
let k3 = "插件MACD强势V999999";
let klines = &obs.K线序列;
if klines.len() < di + 1 {
return vec![Signal::new_empty(&k1, &k2, k3)];
}
let k线 = &klines[klines.len() - di];
let macd = match k线.macd() {
Some(m) => m,
None => return vec![Signal::new_empty(&k1, &k2, k3)],
};
let dif = match macd.DIF { Some(v) => v, None => return vec![Signal::new_empty(&k1, &k2, k3)] };
let dea = match macd.DEA { Some(v) => v, None => return vec![Signal::new_empty(&k1, &k2, k3)] };
if dif > 0.0 && dif > dea {
let score = ((dif - dea).abs() * 1000.0) as i32;
vec![Signal::new(&k1, &k2, k3, "多头强势", "MACD零上", "DIF>DEA", score)]
} else if dif < 0.0 && dif < dea {
vec![Signal::new(&k1, &k2, k3, "空头强势", "MACD零下", "DIF<DEA", 0)]
} else {
vec![Signal::new_empty(&k1, &k2, k3)]
}
}
/// 方式 A 入口:Python 调用 `plugin.init_plugin_manual()`。
#[unsafe(no_mangle)]
pub unsafe extern "C" fn init_plugin_manual() -> i32 {
let name = c"插件MACD强势_V999999";
let template = c"{freq}_D{di}_插件MACD强势V999999";
let func: SignalFn = MACD强势_V999999;
let ret = chanlun_register_signal(name.as_ptr(), template.as_ptr(), func);
eprintln!("[plugin/manual] 注册: {name:?} → 返回码 {ret}");
ret
}
/// 方式 A 清理。
#[unsafe(no_mangle)]
pub unsafe extern "C" fn deinit_plugin_manual() -> i32 {
chanlun_unregister_signal(c"插件MACD强势_V999999".as_ptr())
}
// ═══════════════════════════════════════════════════════════
// 方式 B: #[signal] 宏 + inventory::iter 批量提交
// ═══════════════════════════════════════════════════════════
// 依赖: Cargo.toml 中添加 chanlun-signal-macros
use chanlun::signal::registry::SignalDescriptor;
use chanlun_signal_macros::signal;
/// 插件信号 B1MACD 金叉(与宿主内 `macd_金叉_V260601` 等价)。
#[signal(
name = "插件MACD金叉_V999999",
template = "{freq}_D{di}#MACD#{fast}#{slow}#{signal}_插件MACD金叉V999999",
crate_path = "::chanlun"
)]
fn MACD金叉_V999999(obs: &, params: &HashMap<String, Value>) -> Vec<Signal> {
obs.();
let di = params.get("di").and_then(|v| v.as_i64()).unwrap_or(1) as usize;
let freq = params.get("freq").and_then(|v| v.as_str()).unwrap_or("日线");
let k1 = freq.to_string();
let k2 = "D1#MACD#13#31#11".to_string();
let k3 = "插件MACD金叉V999999";
let klines = &obs.K线序列;
if klines.len() < di + 2 {
return vec![Signal::new_empty(&k1, &k2, k3)];
}
let cur = klines[klines.len() - di].macd();
let prev = klines[klines.len() - di - 1].macd();
let (cur, prev) = match (cur, prev) {
(Some(c), Some(p)) => (c, p),
_ => return vec![Signal::new_empty(&k1, &k2, k3)],
};
let cd = match cur.DIF { Some(v) => v, None => return vec![Signal::new_empty(&k1, &k2, k3)] };
let ce = match cur.DEA { Some(v) => v, None => return vec![Signal::new_empty(&k1, &k2, k3)] };
let pd = match prev.DIF { Some(v) => v, None => return vec![Signal::new_empty(&k1, &k2, k3)] };
let pe = match prev.DEA { Some(v) => v, None => return vec![Signal::new_empty(&k1, &k2, k3)] };
if pd <= pe && cd > ce {
vec![Signal::new(&k1, &k2, k3, "金叉", "插件", "[signal]宏", 80)]
} else if pd >= pe && cd < ce {
vec![Signal::new(&k1, &k2, k3, "死叉", "插件", "[signal]宏", 0)]
} else {
vec![Signal::new_empty(&k1, &k2, k3)]
}
}
/// 插件信号 B2:涨跌停(与宿主内 `bar_zdt_V230331` 等价)。
#[signal(
name = "插件涨跌停_V999999",
template = "{freq}_D{di}_插件涨跌停V999999",
crate_path = "::chanlun"
)]
fn _V999999(obs: &, params: &HashMap<String, Value>) -> Vec<Signal> {
let di = params.get("di").and_then(|v| v.as_i64()).unwrap_or(1) as usize;
let freq = params.get("freq").and_then(|v| v.as_str()).unwrap_or("15分钟");
let k1 = freq.to_string();
let k2 = format!("D{di}");
let k3 = "插件涨跌停V999999";
let klines = &obs.K线序列;
if klines.len() < di + 2 {
return vec![Signal::new_empty(&k1, &k2, k3)];
}
let = &klines[klines.len() - di];
let = &klines[klines.len() - di - 1];
let v1 = if . == . && . >= . {
"涨停"
} else if . == . && . <= . {
"跌停"
} else {
"任意"
};
if v1 == "任意" {
vec![Signal::new_empty(&k1, &k2, k3)]
} else {
vec![Signal::new(&k1, &k2, k3, v1, "插件", "[signal]宏", 0)]
}
}
/// 方式 B 入口:Python 调用 `plugin.init_plugin_macro()`。
///
/// 遍历 `inventory::iter::<SignalDescriptor>`,将 `#[signal]` 宏注册的
/// 所有信号一次性提交到宿主 `DYNAMIC_REGISTRY`。
#[unsafe(no_mangle)]
pub unsafe extern "C" fn init_plugin_macro() -> i32 {
let mut count = 0i32;
for desc in inventory::iter::<SignalDescriptor> {
let Ok(name_c) = std::ffi::CString::new(desc.name) else { continue };
let Ok(tpl_c) = std::ffi::CString::new(desc.template) else { continue };
let ret = chanlun_register_signal(name_c.as_ptr(), tpl_c.as_ptr(), desc.func);
if ret == 0 {
count += 1;
eprintln!("[plugin/macro] ✅ {}{}", desc.name, desc.template);
} else {
eprintln!("[plugin/macro] ❌ {} (err {ret})", desc.name);
}
}
eprintln!("[plugin/macro] 批量注册完成: {count} 个信号");
count
}
/// 方式 B 清理:遍历 inventory 逐个调用 `chanlun_unregister_signal`。
#[unsafe(no_mangle)]
pub unsafe extern "C" fn deinit_plugin_macro() -> i32 {
for desc in inventory::iter::<SignalDescriptor> {
let Ok(name_c) = std::ffi::CString::new(desc.name) else { continue };
chanlun_unregister_signal(name_c.as_ptr());
}
0
}