Deepen Moving Averages family with seven additions (#177)
Deepens the **Moving Averages** family with seven widely-used variants (396 → 403 indicators), the first batch of Part B (family deepening). All are scalar `f64 → f64`: | Indicator | Binding | Notes | |-----------|---------|-------| | `SineWeightedMa` | `SWMA` | symmetric half-cycle sine-weighted window | | `GeometricMa` | `GMA` | rolling geometric mean (log-space average) | | `Ehma` | `EHMA` | exponential Hull MA (Hull construction over EMAs) | | `MedianMa` | `MedianMA` | rolling median, robust to single outliers | | `AdaptiveLaguerreFilter` | `AdaptiveLaguerre` | Ehlers' adaptive Laguerre filter (median-of-normalised-error γ) | | `GeneralizedDema` | `GD` | Tillson's volume-factor double EMA; `v=1` is DEMA, `v=0` is EMA | | `HoltWinters` | `HoltWinters` | Holt's linear double exponential smoothing (level + trend) | LSMA was dropped from the planned set: it already ships as `LinearRegression` (TA-Lib `LINEARREG`, the rolling least-squares endpoint). The five single-period filters use the generated scalar macro bindings; `GeneralizedDema` (period, v) and `HoltWinters` (alpha, beta) use hand-written node/python bindings with the typed wasm macro (precedent `T3` / `Alma`). Full coverage: core modules with per-branch unit tests (100% intent), mod/lib catalogue, FAMILIES group + assert, README + docs counters, CHANGELOG, all three bindings (regenerated `index.d.ts` / `index.js`), fuzz drivers, and the python/node test registries. Local verification: `cargo test -p wickra-core` (lib 3255 + doc 361), `cargo clippy --workspace --all-targets --all-features -D warnings` clean, node `npm run build && npm test` (478), python `pytest` (791).
This commit is contained in:
@@ -28,6 +28,13 @@ function num(v) {
|
||||
// --- Scalar indicators: update(value) vs batch(prices) ---
|
||||
|
||||
const scalarFactories = {
|
||||
HoltWinters: () => new wickra.HoltWinters(0.2, 0.1),
|
||||
GD: () => new wickra.GD(5, 0.7),
|
||||
AdaptiveLaguerre: () => new wickra.AdaptiveLaguerre(13),
|
||||
MedianMA: () => new wickra.MedianMA(14),
|
||||
EHMA: () => new wickra.EHMA(9),
|
||||
GMA: () => new wickra.GMA(14),
|
||||
SWMA: () => new wickra.SWMA(14),
|
||||
Expectancy: () => new wickra.Expectancy(20),
|
||||
WinRate: () => new wickra.WinRate(20),
|
||||
RegimeLabel: () => new wickra.RegimeLabel(5, 20),
|
||||
|
||||
Vendored
+63
@@ -872,6 +872,51 @@ export declare class Expectancy {
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type SineWeightedMaNode = SWMA
|
||||
export declare class SWMA {
|
||||
constructor(period: number)
|
||||
update(value: number): number | null
|
||||
batch(prices: Array<number>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type GeometricMaNode = GMA
|
||||
export declare class GMA {
|
||||
constructor(period: number)
|
||||
update(value: number): number | null
|
||||
batch(prices: Array<number>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type EhmaNode = EHMA
|
||||
export declare class EHMA {
|
||||
constructor(period: number)
|
||||
update(value: number): number | null
|
||||
batch(prices: Array<number>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type MedianMaNode = MedianMA
|
||||
export declare class MedianMA {
|
||||
constructor(period: number)
|
||||
update(value: number): number | null
|
||||
batch(prices: Array<number>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type AdaptiveLaguerreFilterNode = AdaptiveLaguerre
|
||||
export declare class AdaptiveLaguerre {
|
||||
constructor(period: number)
|
||||
update(value: number): number | null
|
||||
batch(prices: Array<number>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type JumpIndicatorNode = JumpIndicator
|
||||
export declare class JumpIndicator {
|
||||
constructor(period: number, threshold: number)
|
||||
@@ -1677,6 +1722,24 @@ export declare class T3 {
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type GeneralizedDemaNode = GD
|
||||
export declare class GD {
|
||||
constructor(period: number, v: number)
|
||||
update(value: number): number | null
|
||||
batch(prices: Array<number>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type HoltWintersNode = HoltWinters
|
||||
export declare class HoltWinters {
|
||||
constructor(alpha: number, beta: number)
|
||||
update(value: number): number | null
|
||||
batch(prices: Array<number>): Array<number>
|
||||
reset(): void
|
||||
isReady(): boolean
|
||||
warmupPeriod(): number
|
||||
}
|
||||
export type TsiNode = TSI
|
||||
export declare class TSI {
|
||||
constructor(long: number, short: number)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -197,6 +197,15 @@ node_scalar_indicator!(
|
||||
node_scalar_indicator!(TrendLabelNode, "TrendLabel", wc::TrendLabel);
|
||||
node_scalar_indicator!(WinRateNode, "WinRate", wc::WinRate);
|
||||
node_scalar_indicator!(ExpectancyNode, "Expectancy", wc::Expectancy);
|
||||
node_scalar_indicator!(SineWeightedMaNode, "SWMA", wc::SineWeightedMa);
|
||||
node_scalar_indicator!(GeometricMaNode, "GMA", wc::GeometricMa);
|
||||
node_scalar_indicator!(EhmaNode, "EHMA", wc::Ehma);
|
||||
node_scalar_indicator!(MedianMaNode, "MedianMA", wc::MedianMa);
|
||||
node_scalar_indicator!(
|
||||
AdaptiveLaguerreFilterNode,
|
||||
"AdaptiveLaguerre",
|
||||
wc::AdaptiveLaguerreFilter
|
||||
);
|
||||
#[napi(js_name = "JumpIndicator")]
|
||||
pub struct JumpIndicatorNode {
|
||||
inner: wc::JumpIndicator,
|
||||
@@ -3794,6 +3803,80 @@ impl T3Node {
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== GD ==============================
|
||||
|
||||
#[napi(js_name = "GD")]
|
||||
pub struct GeneralizedDemaNode {
|
||||
inner: wc::GeneralizedDema,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl GeneralizedDemaNode {
|
||||
#[napi(constructor)]
|
||||
pub fn new(period: u32, v: f64) -> napi::Result<Self> {
|
||||
Ok(Self {
|
||||
inner: wc::GeneralizedDema::new(period as usize, v).map_err(map_err)?,
|
||||
})
|
||||
}
|
||||
#[napi]
|
||||
pub fn update(&mut self, value: f64) -> Option<f64> {
|
||||
self.inner.update(value)
|
||||
}
|
||||
#[napi]
|
||||
pub fn batch(&mut self, prices: Vec<f64>) -> Vec<f64> {
|
||||
flatten(self.inner.batch(&prices))
|
||||
}
|
||||
#[napi]
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
#[napi(js_name = "isReady")]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
#[napi(js_name = "warmupPeriod")]
|
||||
pub fn warmup_period(&self) -> u32 {
|
||||
self.inner.warmup_period() as u32
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== HoltWinters ==============================
|
||||
|
||||
#[napi(js_name = "HoltWinters")]
|
||||
pub struct HoltWintersNode {
|
||||
inner: wc::HoltWinters,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl HoltWintersNode {
|
||||
#[napi(constructor)]
|
||||
pub fn new(alpha: f64, beta: f64) -> napi::Result<Self> {
|
||||
Ok(Self {
|
||||
inner: wc::HoltWinters::new(alpha, beta).map_err(map_err)?,
|
||||
})
|
||||
}
|
||||
#[napi]
|
||||
pub fn update(&mut self, value: f64) -> Option<f64> {
|
||||
self.inner.update(value)
|
||||
}
|
||||
#[napi]
|
||||
pub fn batch(&mut self, prices: Vec<f64>) -> Vec<f64> {
|
||||
flatten(self.inner.batch(&prices))
|
||||
}
|
||||
#[napi]
|
||||
pub fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
#[napi(js_name = "isReady")]
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
#[napi(js_name = "warmupPeriod")]
|
||||
pub fn warmup_period(&self) -> u32 {
|
||||
self.inner.warmup_period() as u32
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== TSI ==============================
|
||||
|
||||
#[napi(js_name = "TSI")]
|
||||
|
||||
@@ -25,6 +25,13 @@ from __future__ import annotations
|
||||
|
||||
from ._wickra import (
|
||||
__version__,
|
||||
HoltWinters,
|
||||
GD,
|
||||
AdaptiveLaguerre,
|
||||
MedianMA,
|
||||
EHMA,
|
||||
GMA,
|
||||
SWMA,
|
||||
Expectancy,
|
||||
WinRate,
|
||||
RegimeLabel,
|
||||
@@ -449,6 +456,13 @@ from ._wickra import (
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"HoltWinters",
|
||||
"GD",
|
||||
"AdaptiveLaguerre",
|
||||
"MedianMA",
|
||||
"EHMA",
|
||||
"GMA",
|
||||
"SWMA",
|
||||
"Expectancy",
|
||||
"WinRate",
|
||||
"RegimeLabel",
|
||||
|
||||
@@ -2354,6 +2354,250 @@ impl PyExpectancy {
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== SineWeightedMa ==============================
|
||||
|
||||
#[pyclass(name = "SWMA", module = "wickra._wickra", skip_from_py_object)]
|
||||
#[derive(Clone)]
|
||||
struct PySineWeightedMa {
|
||||
inner: wc::SineWeightedMa,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PySineWeightedMa {
|
||||
#[new]
|
||||
#[pyo3(signature = (period=14))]
|
||||
fn new(period: usize) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
inner: wc::SineWeightedMa::new(period).map_err(map_err)?,
|
||||
})
|
||||
}
|
||||
fn update(&mut self, value: f64) -> Option<f64> {
|
||||
self.inner.update(value)
|
||||
}
|
||||
fn batch<'py>(
|
||||
&mut self,
|
||||
py: Python<'py>,
|
||||
prices: PyReadonlyArray1<'py, f64>,
|
||||
) -> PyResult<Bound<'py, PyArray1<f64>>> {
|
||||
let s = prices
|
||||
.as_slice()
|
||||
.map_err(|_| PyValueError::new_err(NON_CONTIGUOUS))?;
|
||||
Ok(flatten(self.inner.batch(s)).into_pyarray(py))
|
||||
}
|
||||
#[getter]
|
||||
fn period(&self) -> usize {
|
||||
self.inner.period()
|
||||
}
|
||||
fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
fn __repr__(&self) -> String {
|
||||
format!("SWMA(period={})", self.inner.period())
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== GeometricMa ==============================
|
||||
|
||||
#[pyclass(name = "GMA", module = "wickra._wickra", skip_from_py_object)]
|
||||
#[derive(Clone)]
|
||||
struct PyGeometricMa {
|
||||
inner: wc::GeometricMa,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyGeometricMa {
|
||||
#[new]
|
||||
#[pyo3(signature = (period=14))]
|
||||
fn new(period: usize) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
inner: wc::GeometricMa::new(period).map_err(map_err)?,
|
||||
})
|
||||
}
|
||||
fn update(&mut self, value: f64) -> Option<f64> {
|
||||
self.inner.update(value)
|
||||
}
|
||||
fn batch<'py>(
|
||||
&mut self,
|
||||
py: Python<'py>,
|
||||
prices: PyReadonlyArray1<'py, f64>,
|
||||
) -> PyResult<Bound<'py, PyArray1<f64>>> {
|
||||
let s = prices
|
||||
.as_slice()
|
||||
.map_err(|_| PyValueError::new_err(NON_CONTIGUOUS))?;
|
||||
Ok(flatten(self.inner.batch(s)).into_pyarray(py))
|
||||
}
|
||||
#[getter]
|
||||
fn period(&self) -> usize {
|
||||
self.inner.period()
|
||||
}
|
||||
fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
fn __repr__(&self) -> String {
|
||||
format!("GMA(period={})", self.inner.period())
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== Ehma ==============================
|
||||
|
||||
#[pyclass(name = "EHMA", module = "wickra._wickra", skip_from_py_object)]
|
||||
#[derive(Clone)]
|
||||
struct PyEhma {
|
||||
inner: wc::Ehma,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyEhma {
|
||||
#[new]
|
||||
#[pyo3(signature = (period=9))]
|
||||
fn new(period: usize) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
inner: wc::Ehma::new(period).map_err(map_err)?,
|
||||
})
|
||||
}
|
||||
fn update(&mut self, value: f64) -> Option<f64> {
|
||||
self.inner.update(value)
|
||||
}
|
||||
fn batch<'py>(
|
||||
&mut self,
|
||||
py: Python<'py>,
|
||||
prices: PyReadonlyArray1<'py, f64>,
|
||||
) -> PyResult<Bound<'py, PyArray1<f64>>> {
|
||||
let s = prices
|
||||
.as_slice()
|
||||
.map_err(|_| PyValueError::new_err(NON_CONTIGUOUS))?;
|
||||
Ok(flatten(self.inner.batch(s)).into_pyarray(py))
|
||||
}
|
||||
#[getter]
|
||||
fn period(&self) -> usize {
|
||||
self.inner.period()
|
||||
}
|
||||
fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
fn __repr__(&self) -> String {
|
||||
format!("EHMA(period={})", self.inner.period())
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== MedianMa ==============================
|
||||
|
||||
#[pyclass(name = "MedianMA", module = "wickra._wickra", skip_from_py_object)]
|
||||
#[derive(Clone)]
|
||||
struct PyMedianMa {
|
||||
inner: wc::MedianMa,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyMedianMa {
|
||||
#[new]
|
||||
#[pyo3(signature = (period=14))]
|
||||
fn new(period: usize) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
inner: wc::MedianMa::new(period).map_err(map_err)?,
|
||||
})
|
||||
}
|
||||
fn update(&mut self, value: f64) -> Option<f64> {
|
||||
self.inner.update(value)
|
||||
}
|
||||
fn batch<'py>(
|
||||
&mut self,
|
||||
py: Python<'py>,
|
||||
prices: PyReadonlyArray1<'py, f64>,
|
||||
) -> PyResult<Bound<'py, PyArray1<f64>>> {
|
||||
let s = prices
|
||||
.as_slice()
|
||||
.map_err(|_| PyValueError::new_err(NON_CONTIGUOUS))?;
|
||||
Ok(flatten(self.inner.batch(s)).into_pyarray(py))
|
||||
}
|
||||
#[getter]
|
||||
fn period(&self) -> usize {
|
||||
self.inner.period()
|
||||
}
|
||||
fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
fn __repr__(&self) -> String {
|
||||
format!("MedianMA(period={})", self.inner.period())
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== AdaptiveLaguerreFilter ==============================
|
||||
|
||||
#[pyclass(
|
||||
name = "AdaptiveLaguerre",
|
||||
module = "wickra._wickra",
|
||||
skip_from_py_object
|
||||
)]
|
||||
#[derive(Clone)]
|
||||
struct PyAdaptiveLaguerreFilter {
|
||||
inner: wc::AdaptiveLaguerreFilter,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyAdaptiveLaguerreFilter {
|
||||
#[new]
|
||||
#[pyo3(signature = (period=13))]
|
||||
fn new(period: usize) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
inner: wc::AdaptiveLaguerreFilter::new(period).map_err(map_err)?,
|
||||
})
|
||||
}
|
||||
fn update(&mut self, value: f64) -> Option<f64> {
|
||||
self.inner.update(value)
|
||||
}
|
||||
fn batch<'py>(
|
||||
&mut self,
|
||||
py: Python<'py>,
|
||||
prices: PyReadonlyArray1<'py, f64>,
|
||||
) -> PyResult<Bound<'py, PyArray1<f64>>> {
|
||||
let s = prices
|
||||
.as_slice()
|
||||
.map_err(|_| PyValueError::new_err(NON_CONTIGUOUS))?;
|
||||
Ok(flatten(self.inner.batch(s)).into_pyarray(py))
|
||||
}
|
||||
#[getter]
|
||||
fn period(&self) -> usize {
|
||||
self.inner.period()
|
||||
}
|
||||
fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
fn __repr__(&self) -> String {
|
||||
format!("AdaptiveLaguerre(period={})", self.inner.period())
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== Stochastic ==============================
|
||||
|
||||
#[pyclass(name = "Stochastic", module = "wickra._wickra", skip_from_py_object)]
|
||||
@@ -6197,6 +6441,130 @@ impl PyT3 {
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== GD ==============================
|
||||
|
||||
#[pyclass(name = "GD", module = "wickra._wickra", skip_from_py_object)]
|
||||
#[derive(Clone)]
|
||||
struct PyGeneralizedDema {
|
||||
inner: wc::GeneralizedDema,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyGeneralizedDema {
|
||||
#[new]
|
||||
#[pyo3(signature = (period, v=0.7))]
|
||||
fn new(period: usize, v: f64) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
inner: wc::GeneralizedDema::new(period, v).map_err(map_err)?,
|
||||
})
|
||||
}
|
||||
fn update(&mut self, value: f64) -> Option<f64> {
|
||||
self.inner.update(value)
|
||||
}
|
||||
fn batch<'py>(
|
||||
&mut self,
|
||||
py: Python<'py>,
|
||||
prices: PyReadonlyArray1<'py, f64>,
|
||||
) -> PyResult<Bound<'py, PyArray1<f64>>> {
|
||||
let slice = prices
|
||||
.as_slice()
|
||||
.map_err(|_| PyValueError::new_err(NON_CONTIGUOUS))?;
|
||||
Ok(flatten(self.inner.batch(slice)).into_pyarray(py))
|
||||
}
|
||||
#[getter]
|
||||
fn period(&self) -> usize {
|
||||
self.inner.period()
|
||||
}
|
||||
#[getter]
|
||||
fn volume_factor(&self) -> f64 {
|
||||
self.inner.volume_factor()
|
||||
}
|
||||
fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
fn __repr__(&self) -> String {
|
||||
format!(
|
||||
"GD(period={}, v={})",
|
||||
self.inner.period(),
|
||||
self.inner.volume_factor()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== HoltWinters ==============================
|
||||
|
||||
#[pyclass(name = "HoltWinters", module = "wickra._wickra", skip_from_py_object)]
|
||||
#[derive(Clone)]
|
||||
struct PyHoltWinters {
|
||||
inner: wc::HoltWinters,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl PyHoltWinters {
|
||||
#[new]
|
||||
#[pyo3(signature = (alpha=0.2, beta=0.1))]
|
||||
fn new(alpha: f64, beta: f64) -> PyResult<Self> {
|
||||
Ok(Self {
|
||||
inner: wc::HoltWinters::new(alpha, beta).map_err(map_err)?,
|
||||
})
|
||||
}
|
||||
fn update(&mut self, value: f64) -> Option<f64> {
|
||||
self.inner.update(value)
|
||||
}
|
||||
fn batch<'py>(
|
||||
&mut self,
|
||||
py: Python<'py>,
|
||||
prices: PyReadonlyArray1<'py, f64>,
|
||||
) -> PyResult<Bound<'py, PyArray1<f64>>> {
|
||||
let slice = prices
|
||||
.as_slice()
|
||||
.map_err(|_| PyValueError::new_err(NON_CONTIGUOUS))?;
|
||||
Ok(flatten(self.inner.batch(slice)).into_pyarray(py))
|
||||
}
|
||||
#[getter]
|
||||
fn alpha(&self) -> f64 {
|
||||
self.inner.alpha()
|
||||
}
|
||||
#[getter]
|
||||
fn beta(&self) -> f64 {
|
||||
self.inner.beta()
|
||||
}
|
||||
#[getter]
|
||||
fn level(&self) -> Option<f64> {
|
||||
self.inner.level()
|
||||
}
|
||||
#[getter]
|
||||
fn trend(&self) -> Option<f64> {
|
||||
self.inner.trend()
|
||||
}
|
||||
#[getter]
|
||||
fn value(&self) -> Option<f64> {
|
||||
self.inner.value()
|
||||
}
|
||||
fn reset(&mut self) {
|
||||
self.inner.reset();
|
||||
}
|
||||
fn is_ready(&self) -> bool {
|
||||
self.inner.is_ready()
|
||||
}
|
||||
fn warmup_period(&self) -> usize {
|
||||
self.inner.warmup_period()
|
||||
}
|
||||
fn __repr__(&self) -> String {
|
||||
format!(
|
||||
"HoltWinters(alpha={}, beta={})",
|
||||
self.inner.alpha(),
|
||||
self.inner.beta()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ============================== VWMA ==============================
|
||||
|
||||
#[pyclass(name = "VWMA", module = "wickra._wickra", skip_from_py_object)]
|
||||
@@ -19667,6 +20035,8 @@ fn _wickra(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_class::<PyTrima>()?;
|
||||
m.add_class::<PyZlema>()?;
|
||||
m.add_class::<PyT3>()?;
|
||||
m.add_class::<PyGeneralizedDema>()?;
|
||||
m.add_class::<PyHoltWinters>()?;
|
||||
m.add_class::<PyVwma>()?;
|
||||
m.add_class::<PyMom>()?;
|
||||
m.add_class::<PyCmo>()?;
|
||||
@@ -20031,5 +20401,10 @@ fn _wickra(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_class::<PyRegimeLabel>()?;
|
||||
m.add_class::<PyWinRate>()?;
|
||||
m.add_class::<PyExpectancy>()?;
|
||||
m.add_class::<PySineWeightedMa>()?;
|
||||
m.add_class::<PyGeometricMa>()?;
|
||||
m.add_class::<PyEhma>()?;
|
||||
m.add_class::<PyMedianMa>()?;
|
||||
m.add_class::<PyAdaptiveLaguerreFilter>()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -45,6 +45,13 @@ def ohlcv() -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
||||
# --- Scalar (f64 -> f64) indicators ---------------------------------------
|
||||
|
||||
SCALAR = [
|
||||
(ta.HoltWinters, (0.2, 0.1)),
|
||||
(ta.GD, (5, 0.7)),
|
||||
(ta.AdaptiveLaguerre, (13,)),
|
||||
(ta.MedianMA, (14,)),
|
||||
(ta.EHMA, (9,)),
|
||||
(ta.GMA, (14,)),
|
||||
(ta.SWMA, (14,)),
|
||||
(ta.Expectancy, (20,)),
|
||||
(ta.WinRate, (20,)),
|
||||
(ta.RegimeLabel, (5, 20)),
|
||||
|
||||
@@ -10213,6 +10213,13 @@ wasm_scalar_indicator!(WasmJumpIndicator, "JumpIndicator", wc::JumpIndicator, pe
|
||||
wasm_scalar_indicator!(WasmRegimeLabel, "RegimeLabel", wc::RegimeLabel, vol_period: usize, lookback: usize);
|
||||
wasm_scalar_indicator!(WasmWinRate, "WinRate", wc::WinRate, period: usize);
|
||||
wasm_scalar_indicator!(WasmExpectancy, "Expectancy", wc::Expectancy, period: usize);
|
||||
wasm_scalar_indicator!(WasmSineWeightedMa, "SWMA", wc::SineWeightedMa, period: usize);
|
||||
wasm_scalar_indicator!(WasmGeometricMa, "GMA", wc::GeometricMa, period: usize);
|
||||
wasm_scalar_indicator!(WasmEhma, "EHMA", wc::Ehma, period: usize);
|
||||
wasm_scalar_indicator!(WasmMedianMa, "MedianMA", wc::MedianMa, period: usize);
|
||||
wasm_scalar_indicator!(WasmAdaptiveLaguerreFilter, "AdaptiveLaguerre", wc::AdaptiveLaguerreFilter, period: usize);
|
||||
wasm_scalar_indicator!(WasmGeneralizedDema, "GD", wc::GeneralizedDema, period: usize, v: f64);
|
||||
wasm_scalar_indicator!(WasmHoltWinters, "HoltWinters", wc::HoltWinters, alpha: f64, beta: f64);
|
||||
|
||||
// --- DrawdownDuration: u32 output, no constructor args ---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user