第一版

This commit is contained in:
YuWuKunCheng
2026-05-25 02:16:35 +08:00
parent ff4e6ae570
commit f92eef11fb
37 changed files with 11615 additions and 0 deletions
+204
View File
@@ -0,0 +1,204 @@
use serde::{Deserialize, Serialize};
/// 随机指标 (KDJ)
///
/// 使用滑动窗口 + 逐值平滑进行增量计算
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct {
pub : i64,
pub : f64,
pub : f64,
pub : f64,
pub N: i64,
pub M1: i64,
pub M2: i64,
pub : f64,
pub : f64,
pub RSV: Option<f64>,
pub K: Option<f64>,
pub D: Option<f64>,
pub J: Option<f64>,
pub : Vec<f64>,
pub : Vec<f64>,
pub RSV: Option<f64>,
pub K: Option<f64>,
pub D: Option<f64>,
}
impl Default for {
fn default() -> Self {
Self {
: 0,
: 0.0,
: 0.0,
: 0.0,
N: 9,
M1: 3,
M2: 3,
: 80.0,
: 20.0,
RSV: None,
K: None,
D: None,
J: None,
: Vec::new(),
: Vec::new(),
RSV: None,
K: None,
D: None,
}
}
}
impl {
/// 首次计算 KDJ(无历史数据时)
pub fn (
: f64,
: f64,
: f64,
: i64,
N: i64,
M1: i64,
M2: i64,
: f64,
: f64,
) -> Self {
Self {
: ,
: ,
: ,
: ,
N,
M1,
M2,
,
,
RSV: None,
K: None,
D: None,
J: None,
: vec![],
: vec![],
RSV: None,
K: None,
D: None,
}
}
/// 基于前一个 KDJ 增量计算当前 KDJ
pub fn (KDJ: &Self, : f64, : f64, : f64, : i64) -> Self {
let N = KDJ.N;
let M1 = KDJ.M1;
let M2 = KDJ.M2;
let = KDJ.;
let = KDJ.;
// 更新历史最高价队列
let mut = KDJ..clone();
.push();
if .len() > N as usize {
.remove(0);
}
// 更新历史最低价队列
let mut = KDJ..clone();
.push();
if .len() > N as usize {
.remove(0);
}
// RSV
let RSV = if .len() == N as usize && .len() == N as usize {
let highest = .iter().cloned().fold(f64::NEG_INFINITY, f64::max);
let lowest = .iter().cloned().fold(f64::INFINITY, f64::min);
if (highest - lowest).abs() > f64::EPSILON {
Some(( - lowest) / (highest - lowest) * 100.0)
} else {
Some(50.0)
}
} else {
None
};
// K值
let K = match RSV {
Some(rsv) => match KDJ.K {
None => Some(rsv),
Some(prev_k) => Some((prev_k * (M1 - 1) as f64 + rsv) / M1 as f64),
},
None => KDJ.K,
};
// D值
let D = match K {
Some(k) => match KDJ.D {
None => Some(k),
Some(prev_d) => Some((prev_d * (M2 - 1) as f64 + k) / M2 as f64),
},
None => KDJ.D,
};
// J值
let J = match (K, D) {
(Some(k), Some(d)) => Some(3.0 * k - 2.0 * d),
_ => None,
};
Self {
: ,
: ,
: ,
: ,
N,
M1,
M2,
,
,
RSV,
K,
D,
J,
: ,
: ,
RSV: RSV,
K: K,
D: D,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_first_calc() {
let kdj = ::(110.0, 90.0, 100.0, 1000, 9, 3, 3, 80.0, 20.0);
assert_eq!(kdj., vec![110.0]);
assert_eq!(kdj., vec![90.0]);
assert_eq!(kdj.K, None);
}
#[test]
fn test_incremental_after_n_bars() {
let mut kdj = ::(110.0, 90.0, 100.0, 1000, 5, 3, 3, 80.0, 20.0);
// 喂入足够数据填充窗口
let data = [
(112.0, 91.0, 105.0),
(115.0, 93.0, 110.0),
(113.0, 95.0, 108.0),
(116.0, 98.0, 112.0),
(118.0, 100.0, 115.0),
];
for (i, (, , )) in data.iter().enumerate() {
kdj = ::(&kdj, *, *, *, 1001 + i as i64);
}
// 窗口填满后 KDJ 应有值
assert!(kdj.K.is_some());
assert!(kdj.D.is_some());
assert!(kdj.J.is_some());
}
}
+149
View File
@@ -0,0 +1,149 @@
use serde::{Deserialize, Serialize};
/// 平滑异同移动平均线 (MACD)
///
/// 使用 EMA 递推算法进行增量计算
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct 线 {
pub : i64,
pub : f64,
pub 线: i64,
pub 线: i64,
pub : i64,
pub DIF: Option<f64>,
pub DEA: Option<f64>,
#[serde(rename = "MACD柱")]
#[serde(default)]
pub MACD柱: f64,
pub 线EMA: Option<f64>,
pub 线EMA: Option<f64>,
pub DEA_EMA: Option<f64>,
}
impl Default for 线 {
fn default() -> Self {
Self {
: 0,
: 0.0,
线: 12,
线: 26,
: 9,
DIF: None,
DEA: None,
MACD柱: 0.0,
线EMA: None,
线EMA: None,
DEA_EMA: None,
}
}
}
fn (: i64) -> f64 {
2.0 / ( as f64 + 1.0)
}
impl 线 {
/// 首次计算 MACD 指标(无历史数据时使用)
pub fn (: f64, : i64, 线: i64, 线: i64, : i64) -> Self {
let 线EMA = ;
let 线EMA = ;
let DIF = 线EMA - 线EMA;
let DEA_EMA = DIF;
let MACD柱 = DIF - DEA_EMA;
Self {
: ,
: ,
线,
线,
,
DIF: Some(DIF),
DEA: Some(DEA_EMA),
MACD柱,
线EMA: Some(线EMA),
线EMA: Some(线EMA),
DEA_EMA: Some(DEA_EMA),
}
}
/// 基于前一个 MACD 指标增量计算当前 MACD
pub fn (MACD: &Self, : f64, : i64) -> Self {
// 快线 EMA
let 线EMA = match MACD.线EMA {
Some(prev) => {
* (MACD.线)
+ prev * ((MACD.线 - 1) as f64 / (MACD.线 + 1) as f64)
}
None => ,
};
// 慢线 EMA
let 线EMA = match MACD.线EMA {
Some(prev) => {
* (MACD.线)
+ prev * ((MACD.线 - 1) as f64 / (MACD.线 + 1) as f64)
}
None => ,
};
// DIF
let DIF = 线EMA - 线EMA;
// DEA_EMA
let DEA_EMA = match MACD.DEA_EMA {
Some(prev) => {
DIF * (MACD.)
+ prev * ((MACD. - 1) as f64 / (MACD. + 1) as f64)
}
None => DIF,
};
// MACD 柱 (注意: Python 版没有 ×2)
let MACD柱 = DIF - DEA_EMA;
Self {
: ,
: ,
线: MACD.线,
线: MACD.线,
: MACD.,
DIF: Some(DIF),
DEA: Some(DEA_EMA),
MACD柱,
线EMA: Some(线EMA),
线EMA: Some(线EMA),
DEA_EMA: Some(DEA_EMA),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_first_calc() {
let macd = 线::(100.0, 1000, 12, 26, 9);
assert_eq!(macd.DIF, Some(0.0));
assert_eq!(macd.MACD柱, 0.0);
assert_eq!(macd.线EMA, Some(100.0));
assert_eq!(macd.线EMA, Some(100.0));
}
#[test]
fn test_incremental_calc() {
let first = 线::(100.0, 1000, 12, 26, 9);
// 价格上升
let second = 线::(&first, 102.0, 1001);
assert!(second.DIF.unwrap() > 0.0);
// 快线EMA 应该比慢线EMA 变化更快
assert!(second.线EMA.unwrap() > second.线EMA.unwrap());
// 价格下降
let third = 线::(&second, 98.0, 1002);
// DIF 应该变小
assert!(third.DIF.unwrap() < second.DIF.unwrap());
}
}
+21
View File
@@ -0,0 +1,21 @@
pub mod macd;
pub mod kdj;
pub mod rsi;
pub use macd::线;
pub use kdj::;
pub use rsi::;
/// K线取值 —— 根据计算方式从K线提取对应的价格
pub fn K线取值(: f64, : f64, : f64, : f64, : &str) -> f64 {
match {
"" => ,
"" => ,
"" => ,
"" => ,
"高低均值" => ( + ) / 2.0,
"高低收均值" => ( + + ) / 3.0,
"开高低收均值" => ( + + + ) / 4.0,
_ => ,
}
}
+180
View File
@@ -0,0 +1,180 @@
use serde::{Deserialize, Serialize};
/// 相对强弱指数 (RSI)
///
/// 使用 Wilder 平滑(RMA)进行增量计算
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct {
pub : i64,
pub : f64,
pub : i64,
pub : f64,
pub : f64,
pub RSI_SMA周期: Option<i64>,
pub RSI: Option<f64>,
pub : Option<f64>,
pub : Option<f64>,
pub : f64,
pub : f64,
pub : f64,
pub RSI_SMA: Option<f64>,
pub RSI历史队列: Vec<f64>,
}
impl Default for {
fn default() -> Self {
Self {
: 0,
: 0.0,
: 14,
: 70.0,
: 30.0,
RSI_SMA周期: None,
RSI: None,
: None,
: None,
: 0.0,
: 0.0,
: 0.0,
RSI_SMA: None,
RSI历史队列: Vec::new(),
}
}
}
impl {
/// 首次计算 RSI(历史数据不足时)
pub fn (
: f64,
: i64,
: i64,
: f64,
: f64,
RSI_SMA周期: Option<i64>,
) -> Self {
Self {
: ,
: ,
,
,
,
RSI_SMA周期,
RSI: None,
: Some(0.0),
: Some(0.0),
: 0.0,
: 0.0,
: 1.0 / as f64,
RSI_SMA: None,
RSI历史队列: Vec::new(),
}
}
/// 基于前一个 RSI 增量计算当前 RSI
pub fn (RSI: &Self, : f64, : i64) -> Self {
let = RSI.;
let = RSI.;
let = RSI.;
let RSI_SMA周期 = RSI.RSI_SMA周期;
let = 1.0 / as f64;
// 价格变化
let = - RSI.;
let = .max(0.0);
let = (-).max(0.0);
// Wilder 平滑
let (, ) = match (RSI., RSI.) {
(Some(prev_up), Some(prev_down)) => {
let avg_up = prev_up * (1.0 - ) + * ;
let avg_down = prev_down * (1.0 - ) + * ;
(avg_up, avg_down)
}
_ => (, ),
};
// RSI
let RSI = if == 0.0 {
if > 0.0 {
100.0
} else {
50.0
}
} else {
let RS = / ;
100.0 - (100.0 / (1.0 + RS))
};
// RSI_SMA
let (RSI_SMA, RSI历史队列) = match RSI_SMA周期 {
Some(sma周期) if sma周期 > 0 => {
let mut = RSI.RSI历史队列.clone();
.push(RSI);
if .len() > sma周期 as usize {
.remove(0);
}
let sma = if .is_empty() {
None
} else {
Some(.iter().sum::<f64>() / .len() as f64)
};
(sma, )
}
_ => (None, Vec::new()),
};
Self {
: ,
: ,
,
,
,
RSI_SMA周期,
RSI: Some(RSI),
: Some(),
: Some(),
: ,
: ,
,
RSI_SMA,
RSI历史队列,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_first_calc() {
let rsi = ::(100.0, 1000, 14, 70.0, 30.0, None);
assert_eq!(rsi.RSI, None);
assert_eq!(rsi., 1.0 / 14.0);
}
#[test]
fn test_incremental_calc() {
let first = ::(100.0, 1000, 14, 70.0, 30.0, None);
let second = ::(&first, 102.0, 1001);
// 价格上涨 → RSI > 50
assert!(second.RSI.unwrap() > 50.0);
let third = ::(&second, 98.0, 1002);
// 价格低于之前 → RSI 下降
assert!(third.RSI.unwrap() < second.RSI.unwrap());
}
#[test]
fn test_rsi_sma() {
let mut rsi = ::(100.0, 1000, 14, 70.0, 30.0, Some(5));
// 喂入多根K线来积累RSI历史队列
let prices = [102.0, 103.0, 101.0, 104.0, 105.0, 103.0, 106.0];
for (i, price) in prices.iter().enumerate() {
rsi = ::(&rsi, *price, 1001 + i as i64);
}
// SMA 应该已被计算(队列够长)
assert!(rsi.RSI_SMA.is_some());
}
}