feat: microstructure price-impact & depth indicators (part 3 of 4) (#122)

* feat: effective spread microstructure indicator (part 3 of 4)

* feat: realized spread microstructure indicator (part 3 of 4)

* feat: kyle's lambda microstructure indicator (part 3 of 4)

* feat: depth slope microstructure indicator (part 3 of 4)
This commit is contained in:
kingchenc
2026-06-01 19:45:38 +02:00
committed by GitHub
parent b5d9e47a2e
commit 4f11df0e33
25 changed files with 1898 additions and 39 deletions
+144
View File
@@ -6421,6 +6421,7 @@ wasm_ob_indicator!(
);
wasm_ob_indicator!(WasmMicroprice, wc::Microprice, Microprice);
wasm_ob_indicator!(WasmQuotedSpread, wc::QuotedSpread, QuotedSpread);
wasm_ob_indicator!(WasmDepthSlope, wc::DepthSlope, DepthSlope);
// Top-N imbalance carries a `levels` parameter, so it is hand-written.
#[wasm_bindgen(js_name = OrderBookImbalanceTopN)]
@@ -6555,6 +6556,149 @@ impl WasmTradeImbalance {
}
}
// ============================== Microstructure: Price Impact ==============================
//
// Price-impact indicators consume a trade paired with the mid prevailing at
// execution. Each `update(price, size, isBuy, mid)` takes one such trade-quote
// (`isBuy=true` for a buyer-initiated trade) — the streaming model for a live
// browser trade feed. Batch over a tape is provided by the Python and Node
// bindings.
fn build_trade_quote(
price: f64,
size: f64,
is_buy: bool,
mid: f64,
) -> Result<wc::TradeQuote, JsError> {
let trade = build_trade(price, size, is_buy)?;
wc::TradeQuote::new(trade, mid).map_err(map_err)
}
macro_rules! wasm_trade_quote_indicator {
($wasm:ident, $inner:ty, $js:ident) => {
#[wasm_bindgen(js_name = $js)]
pub struct $wasm {
inner: $inner,
}
impl Default for $wasm {
fn default() -> Self {
Self::new()
}
}
#[wasm_bindgen(js_class = $js)]
impl $wasm {
#[wasm_bindgen(constructor)]
pub fn new() -> $wasm {
Self {
inner: <$inner>::new(),
}
}
pub fn update(
&mut self,
price: f64,
size: f64,
is_buy: bool,
mid: f64,
) -> Result<Option<f64>, JsError> {
Ok(self
.inner
.update(build_trade_quote(price, size, is_buy, mid)?))
}
pub fn reset(&mut self) {
self.inner.reset();
}
#[wasm_bindgen(js_name = isReady)]
pub fn is_ready(&self) -> bool {
self.inner.is_ready()
}
#[wasm_bindgen(js_name = warmupPeriod)]
pub fn warmup_period(&self) -> usize {
self.inner.warmup_period()
}
}
};
}
wasm_trade_quote_indicator!(WasmEffectiveSpread, wc::EffectiveSpread, EffectiveSpread);
// Realized spread carries a `horizon` parameter, so it is hand-written.
#[wasm_bindgen(js_name = RealizedSpread)]
pub struct WasmRealizedSpread {
inner: wc::RealizedSpread,
}
#[wasm_bindgen(js_class = RealizedSpread)]
impl WasmRealizedSpread {
#[wasm_bindgen(constructor)]
pub fn new(horizon: usize) -> Result<WasmRealizedSpread, JsError> {
Ok(Self {
inner: wc::RealizedSpread::new(horizon).map_err(map_err)?,
})
}
pub fn update(
&mut self,
price: f64,
size: f64,
is_buy: bool,
mid: f64,
) -> Result<Option<f64>, JsError> {
Ok(self
.inner
.update(build_trade_quote(price, size, is_buy, mid)?))
}
pub fn reset(&mut self) {
self.inner.reset();
}
#[wasm_bindgen(js_name = isReady)]
pub fn is_ready(&self) -> bool {
self.inner.is_ready()
}
#[wasm_bindgen(js_name = warmupPeriod)]
pub fn warmup_period(&self) -> usize {
self.inner.warmup_period()
}
}
// Kyle's lambda carries a `window` parameter, so it is hand-written.
#[wasm_bindgen(js_name = KylesLambda)]
pub struct WasmKylesLambda {
inner: wc::KylesLambda,
}
#[wasm_bindgen(js_class = KylesLambda)]
impl WasmKylesLambda {
#[wasm_bindgen(constructor)]
pub fn new(window: usize) -> Result<WasmKylesLambda, JsError> {
Ok(Self {
inner: wc::KylesLambda::new(window).map_err(map_err)?,
})
}
pub fn update(
&mut self,
price: f64,
size: f64,
is_buy: bool,
mid: f64,
) -> Result<Option<f64>, JsError> {
Ok(self
.inner
.update(build_trade_quote(price, size, is_buy, mid)?))
}
pub fn reset(&mut self) {
self.inner.reset();
}
#[wasm_bindgen(js_name = isReady)]
pub fn is_ready(&self) -> bool {
self.inner.is_ready()
}
#[wasm_bindgen(js_name = warmupPeriod)]
pub fn warmup_period(&self) -> usize {
self.inner.warmup_period()
}
}
#[cfg(test)]
mod tests {
use super::*;