refactor: remove finance-specific code from point_processes module
- Removed order_flow.rs (UnifiedTheoryParams, OrderFlowAnalyzer, MarketImpact) - Removed analyze_order_flow, unified_theory_params, market_impact Python bindings - Updated mod.rs documentation to be generic (no trading references) - Kept general-purpose: Hawkes, fBM, mfBM, Mittag-Leffler, Hurst estimation Finance-specific code moved to rust-hft-arbitrage-lab-internal
This commit is contained in:
+39
-29
@@ -1,52 +1,67 @@
|
||||
//! Point Processes Module for Order Flow Modeling
|
||||
//! Point Processes Module
|
||||
//!
|
||||
//! This module implements the mathematical framework from "A Unified Theory of
|
||||
//! Order Flow, Market Impact, and Volatility" (Muhle-Karbe et al., 2026).
|
||||
//! This module provides general-purpose implementations of point processes,
|
||||
//! fractional Brownian motion, and related mathematical tools.
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! The module provides tools for modeling order flow in financial markets using:
|
||||
//!
|
||||
//! - **Hawkes Processes**: Self-exciting point processes for core and reaction flows
|
||||
//! - **Hawkes Processes**: Self-exciting point processes with flexible kernels
|
||||
//! - **Excitation Kernels**: Power-law and exponential kernels for temporal dependence
|
||||
//! - **Mixed Fractional Brownian Motion**: Scaling limits of aggregate order flow
|
||||
//! - **Mittag-Leffler Functions**: Key functions for scaling limit analysis
|
||||
//! - **Order Flow Analysis**: Signed/unsigned flow, Hurst estimation, market impact
|
||||
//! - **Fractional Brownian Motion (fBM)**: Long-memory Gaussian processes
|
||||
//! - **Mixed Fractional Brownian Motion (mfBM)**: BM + fBM combinations
|
||||
//! - **Mittag-Leffler Functions**: Special functions for scaling limit analysis
|
||||
//! - **Hurst Estimation**: R/S analysis and scale-dependent methods
|
||||
//!
|
||||
//! # Key Relationships (Unified Theory)
|
||||
//! # Mathematical Background
|
||||
//!
|
||||
//! All quantities are determined by a single parameter H₀ ≈ 3/4:
|
||||
//! ## Hawkes Processes
|
||||
//!
|
||||
//! - Signed order flow: Hurst index H₀
|
||||
//! - Unsigned volume: Hurst index H₀ - 1/2 (rough, ~0.25)
|
||||
//! - Volatility: Hurst index 2H₀ - 3/2 (~0 for H₀=3/4)
|
||||
//! - Market impact: power law exponent 2 - 2H₀ (~0.5, square-root law)
|
||||
//! A Hawkes process is a self-exciting point process with intensity:
|
||||
//!
|
||||
//! $$\lambda(t) = \nu + \sum_{t_i < t} \phi(t - t_i)$$
|
||||
//!
|
||||
//! where $\nu$ is the baseline intensity and $\phi$ is the excitation kernel.
|
||||
//!
|
||||
//! Supported kernels:
|
||||
//! - **Exponential**: $\phi(t) = \alpha e^{-\beta t}$ (short memory)
|
||||
//! - **Power-law**: $\phi(t) = K_0 (1+t)^{-(1+\alpha_0)}$ (long memory)
|
||||
//!
|
||||
//! ## Fractional Brownian Motion
|
||||
//!
|
||||
//! fBM $B^H_t$ with Hurst parameter $H \in (0,1)$ has covariance:
|
||||
//!
|
||||
//! $$Cov(B^H_s, B^H_t) = \frac{1}{2}(|t|^{2H} + |s|^{2H} - |t-s|^{2H})$$
|
||||
//!
|
||||
//! - $H < 0.5$: Anti-persistent (mean-reverting)
|
||||
//! - $H = 0.5$: Standard Brownian motion
|
||||
//! - $H > 0.5$: Persistent (trending)
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! use optimizr::point_processes::{
|
||||
//! HawkesProcess, PowerLawKernel, OrderFlowAnalyzer
|
||||
//! HawkesProcess, PowerLawKernel, FractionalBM
|
||||
//! };
|
||||
//!
|
||||
//! // Create a Hawkes process for core order flow
|
||||
//! // Create a Hawkes process with power-law kernel
|
||||
//! let kernel = PowerLawKernel::new(0.5, 1.0); // α₀ = 0.5, K₀ = 1.0
|
||||
//! let hawkes = HawkesProcess::new(0.1, kernel); // ν = 0.1
|
||||
//! let hawkes = HawkesProcess::new(0.1, kernel); // baseline ν = 0.1
|
||||
//!
|
||||
//! // Simulate order arrivals
|
||||
//! // Simulate point process
|
||||
//! let arrivals = hawkes.simulate(1000.0, Some(42));
|
||||
//!
|
||||
//! // Analyze order flow
|
||||
//! let analyzer = OrderFlowAnalyzer::new();
|
||||
//! let h0 = analyzer.estimate_h0(&arrivals);
|
||||
//! println!("Estimated H₀: {:.4}", h0);
|
||||
//! // Simulate fBM with H = 0.75
|
||||
//! let mut fbm = FractionalBM::new(0.75);
|
||||
//! let path = fbm.simulate_hosking(1000, 1.0, Some(42));
|
||||
//!
|
||||
//! // Estimate Hurst exponent
|
||||
//! let h_est = FractionalBM::estimate_hurst(&path);
|
||||
//! ```
|
||||
|
||||
mod kernels;
|
||||
mod hawkes;
|
||||
mod mittag_leffler;
|
||||
mod mixed_fbm;
|
||||
mod order_flow;
|
||||
|
||||
#[cfg(feature = "python-bindings")]
|
||||
pub mod python_bindings;
|
||||
@@ -56,8 +71,3 @@ pub use kernels::{ExcitationKernel, PowerLawKernel, ExponentialKernel};
|
||||
pub use hawkes::{HawkesProcess, HawkesProcessConfig, BivariateHawkes};
|
||||
pub use mittag_leffler::{mittag_leffler, mittag_leffler_derivative, f_alpha_lambda};
|
||||
pub use mixed_fbm::{MixedFractionalBM, FractionalBM};
|
||||
pub use order_flow::{
|
||||
OrderFlowAnalyzer, OrderFlowMetrics, UnifiedTheoryParams,
|
||||
signed_order_flow, unsigned_volume, market_impact_exponent,
|
||||
volatility_hurst, volume_hurst
|
||||
};
|
||||
|
||||
@@ -1,452 +0,0 @@
|
||||
//! Order Flow Analysis Module
|
||||
//!
|
||||
//! Implements the unified theory framework for analyzing signed and unsigned
|
||||
//! order flow, estimating H₀, and deriving market impact and volatility.
|
||||
//!
|
||||
//! # Key Relationships (from unified theory)
|
||||
//!
|
||||
//! Given H₀ ≈ 3/4 (persistence of core flow):
|
||||
//! - Signed order flow: Hurst index H₀
|
||||
//! - Unsigned volume: Hurst index H₁ = H₀ - 1/2 ≈ 0.25 (rough)
|
||||
//! - Volatility: Hurst index H_vol = 2H₀ - 3/2 ≈ 0 (very rough)
|
||||
//! - Market impact: power law exponent δ = 2 - 2H₀ ≈ 0.5 (square root)
|
||||
|
||||
use crate::point_processes::mixed_fbm::{FractionalBM, MixedFractionalBM};
|
||||
use std::f64::consts::PI;
|
||||
|
||||
/// Parameters from the unified theory
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UnifiedTheoryParams {
|
||||
/// H₀: Hurst index of signed order flow / core flow persistence
|
||||
/// Typically H₀ ≈ 0.75 empirically
|
||||
pub h0: f64,
|
||||
|
||||
/// μ₀: Baseline intensity scaling constant
|
||||
pub mu0: f64,
|
||||
|
||||
/// λ₀: Decay rate parameter
|
||||
pub lambda0: f64,
|
||||
}
|
||||
|
||||
impl UnifiedTheoryParams {
|
||||
pub fn new(h0: f64) -> Self {
|
||||
assert!(h0 > 0.5 && h0 < 1.0, "H0 must be in (0.5, 1)");
|
||||
Self {
|
||||
h0,
|
||||
mu0: 1.0,
|
||||
lambda0: 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create with typical empirical value H₀ ≈ 0.75
|
||||
pub fn empirical() -> Self {
|
||||
Self::new(0.75)
|
||||
}
|
||||
|
||||
/// Hurst index of unsigned volume: H₁ = H₀ - 1/2
|
||||
pub fn volume_hurst(&self) -> f64 {
|
||||
self.h0 - 0.5
|
||||
}
|
||||
|
||||
/// Hurst index of volatility: H_vol = 2H₀ - 3/2
|
||||
pub fn volatility_hurst(&self) -> f64 {
|
||||
2.0 * self.h0 - 1.5
|
||||
}
|
||||
|
||||
/// Market impact exponent: δ = 2 - 2H₀
|
||||
pub fn impact_exponent(&self) -> f64 {
|
||||
2.0 - 2.0 * self.h0
|
||||
}
|
||||
|
||||
/// Check if mfBM is semimartingale (H₀ > 3/4)
|
||||
pub fn is_semimartingale(&self) -> bool {
|
||||
self.h0 > 0.75
|
||||
}
|
||||
|
||||
/// α₀ (tail exponent): H₀ = 2α₀, so α₀ = H₀/2
|
||||
pub fn alpha0(&self) -> f64 {
|
||||
self.h0 / 2.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate signed order flow Hurst index (= H₀)
|
||||
pub fn signed_order_flow(h0: f64) -> f64 {
|
||||
h0
|
||||
}
|
||||
|
||||
/// Calculate unsigned volume Hurst index: H₁ = H₀ - 1/2
|
||||
pub fn unsigned_volume(h0: f64) -> f64 {
|
||||
h0 - 0.5
|
||||
}
|
||||
|
||||
/// Alias for unsigned_volume
|
||||
pub fn volume_hurst(h0: f64) -> f64 {
|
||||
unsigned_volume(h0)
|
||||
}
|
||||
|
||||
/// Calculate volatility Hurst index: H_vol = 2H₀ - 3/2
|
||||
pub fn volatility_hurst(h0: f64) -> f64 {
|
||||
2.0 * h0 - 1.5
|
||||
}
|
||||
|
||||
/// Calculate market impact exponent: δ = 2 - 2H₀
|
||||
/// Impact ~ Q^δ where Q is order size
|
||||
pub fn market_impact_exponent(h0: f64) -> f64 {
|
||||
2.0 - 2.0 * h0
|
||||
}
|
||||
|
||||
/// Order flow metrics computed from data
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OrderFlowMetrics {
|
||||
/// Estimated H₀ (core flow persistence)
|
||||
pub h0: f64,
|
||||
|
||||
/// Estimated Hurst of signed flow (under fBM assumption)
|
||||
pub h_signed_fbm: f64,
|
||||
|
||||
/// Estimated Hurst of signed flow (under mfBM assumption)
|
||||
pub h_signed_mfbm: f64,
|
||||
|
||||
/// Estimated Hurst of unsigned volume
|
||||
pub h_unsigned: f64,
|
||||
|
||||
/// Total signed flow
|
||||
pub total_signed: f64,
|
||||
|
||||
/// Total unsigned volume
|
||||
pub total_unsigned: f64,
|
||||
|
||||
/// Implied volatility Hurst
|
||||
pub h_volatility: f64,
|
||||
|
||||
/// Implied impact exponent
|
||||
pub impact_exponent: f64,
|
||||
|
||||
/// Autocorrelation at lag 1
|
||||
pub acf_1: f64,
|
||||
|
||||
/// Scale-dependent Hurst estimates
|
||||
pub scale_hurst: Vec<(usize, f64)>,
|
||||
}
|
||||
|
||||
/// Analyzer for order flow data
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct OrderFlowAnalyzer {
|
||||
/// Whether to compute scale-dependent statistics
|
||||
pub compute_scales: bool,
|
||||
|
||||
/// Scales for multi-scale analysis
|
||||
pub scales: Vec<usize>,
|
||||
}
|
||||
|
||||
impl OrderFlowAnalyzer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
compute_scales: true,
|
||||
scales: vec![10, 50, 100, 500, 1000, 2000, 5000],
|
||||
}
|
||||
}
|
||||
|
||||
/// Analyze signed order flow
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `flow` - Signed order flow data (positive = buy, negative = sell)
|
||||
pub fn analyze_signed_flow(&self, flow: &[f64]) -> OrderFlowMetrics {
|
||||
let n = flow.len();
|
||||
if n < 100 {
|
||||
return self.default_metrics();
|
||||
}
|
||||
|
||||
// Cumulative signed flow
|
||||
let mut cum_flow = vec![0.0; n + 1];
|
||||
for i in 0..n {
|
||||
cum_flow[i + 1] = cum_flow[i] + flow[i];
|
||||
}
|
||||
|
||||
// Estimate H under fBM assumption (R/S analysis)
|
||||
let h_fbm = FractionalBM::estimate_hurst(&cum_flow);
|
||||
|
||||
// Estimate H under mfBM assumption (scale-dependent)
|
||||
let scale_hurst = MixedFractionalBM::scale_dependent_hurst(
|
||||
&cum_flow,
|
||||
&self.scales,
|
||||
);
|
||||
|
||||
// Average of high-frequency estimates (mfBM martingale component dominates)
|
||||
let h_mfbm_hf: f64 = scale_hurst
|
||||
.iter()
|
||||
.filter(|(s, _)| *s < 100)
|
||||
.map(|(_, h)| *h)
|
||||
.sum::<f64>() / scale_hurst.iter().filter(|(s, _)| *s < 100).count().max(1) as f64;
|
||||
|
||||
// Average of low-frequency estimates (fBM component dominates)
|
||||
let h_mfbm_lf: f64 = scale_hurst
|
||||
.iter()
|
||||
.filter(|(s, _)| *s >= 500)
|
||||
.map(|(_, h)| *h)
|
||||
.sum::<f64>() / scale_hurst.iter().filter(|(s, _)| *s >= 500).count().max(1) as f64;
|
||||
|
||||
// H₀ estimate: use low-frequency Hurst (persistent component)
|
||||
let h0 = if h_mfbm_lf > 0.5 { h_mfbm_lf } else { h_fbm };
|
||||
|
||||
// Unsigned volume (absolute values)
|
||||
let unsigned: Vec<f64> = flow.iter().map(|x| x.abs()).collect();
|
||||
let cum_unsigned: Vec<f64> = unsigned.iter()
|
||||
.scan(0.0, |acc, &x| { *acc += x; Some(*acc) })
|
||||
.collect();
|
||||
let h_unsigned = estimate_hurst_variance_ratio(&cum_unsigned);
|
||||
|
||||
// Autocorrelation
|
||||
let mean: f64 = flow.iter().sum::<f64>() / n as f64;
|
||||
let var: f64 = flow.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / n as f64;
|
||||
let acf_1 = if var > 1e-10 {
|
||||
let cov: f64 = flow[..n-1].iter().zip(flow[1..].iter())
|
||||
.map(|(x, y)| (x - mean) * (y - mean))
|
||||
.sum::<f64>() / (n - 1) as f64;
|
||||
cov / var
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
OrderFlowMetrics {
|
||||
h0,
|
||||
h_signed_fbm: h_fbm,
|
||||
h_signed_mfbm: h_mfbm_lf,
|
||||
h_unsigned,
|
||||
total_signed: cum_flow.last().copied().unwrap_or(0.0),
|
||||
total_unsigned: cum_unsigned.last().copied().unwrap_or(0.0),
|
||||
h_volatility: volatility_hurst(h0),
|
||||
impact_exponent: market_impact_exponent(h0),
|
||||
acf_1,
|
||||
scale_hurst,
|
||||
}
|
||||
}
|
||||
|
||||
/// Analyze order arrival times
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `buy_times` - Buy order arrival times
|
||||
/// * `sell_times` - Sell order arrival times
|
||||
/// * `t_max` - Maximum time
|
||||
/// * `bin_size` - Time bin for aggregation
|
||||
pub fn analyze_arrivals(
|
||||
&self,
|
||||
buy_times: &[f64],
|
||||
sell_times: &[f64],
|
||||
t_max: f64,
|
||||
bin_size: f64,
|
||||
) -> OrderFlowMetrics {
|
||||
// Bin the arrivals
|
||||
let n_bins = (t_max / bin_size).ceil() as usize;
|
||||
let mut signed_flow = vec![0.0; n_bins];
|
||||
|
||||
for &t in buy_times {
|
||||
let bin = ((t / bin_size).floor() as usize).min(n_bins - 1);
|
||||
signed_flow[bin] += 1.0;
|
||||
}
|
||||
for &t in sell_times {
|
||||
let bin = ((t / bin_size).floor() as usize).min(n_bins - 1);
|
||||
signed_flow[bin] -= 1.0;
|
||||
}
|
||||
|
||||
self.analyze_signed_flow(&signed_flow)
|
||||
}
|
||||
|
||||
/// Estimate H₀ from signed order flow data
|
||||
pub fn estimate_h0(&self, flow: &[f64]) -> f64 {
|
||||
self.analyze_signed_flow(flow).h0
|
||||
}
|
||||
|
||||
fn default_metrics(&self) -> OrderFlowMetrics {
|
||||
OrderFlowMetrics {
|
||||
h0: 0.75,
|
||||
h_signed_fbm: 0.5,
|
||||
h_signed_mfbm: 0.75,
|
||||
h_unsigned: 0.25,
|
||||
total_signed: 0.0,
|
||||
total_unsigned: 0.0,
|
||||
h_volatility: 0.0,
|
||||
impact_exponent: 0.5,
|
||||
acf_1: 0.0,
|
||||
scale_hurst: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Estimate Hurst exponent using variance ratio method
|
||||
fn estimate_hurst_variance_ratio(data: &[f64]) -> f64 {
|
||||
let n = data.len();
|
||||
if n < 50 {
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
let scales = [5, 10, 20, 40, 80, 160];
|
||||
let mut log_scales = Vec::new();
|
||||
let mut log_vars = Vec::new();
|
||||
|
||||
for &s in &scales {
|
||||
if s >= n / 4 {
|
||||
break;
|
||||
}
|
||||
|
||||
// Compute increments at scale s
|
||||
let increments: Vec<f64> = (s..n).map(|i| data[i] - data[i - s]).collect();
|
||||
if increments.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let var: f64 = increments.iter().map(|x| x.powi(2)).sum::<f64>() / increments.len() as f64;
|
||||
if var > 1e-15 {
|
||||
log_scales.push((s as f64).ln());
|
||||
log_vars.push(var.ln());
|
||||
}
|
||||
}
|
||||
|
||||
if log_scales.len() < 3 {
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
// Linear regression: log(var) = 2H * log(scale) + const
|
||||
let n_pts = log_scales.len() as f64;
|
||||
let mean_x: f64 = log_scales.iter().sum::<f64>() / n_pts;
|
||||
let mean_y: f64 = log_vars.iter().sum::<f64>() / n_pts;
|
||||
|
||||
let mut num = 0.0;
|
||||
let mut den = 0.0;
|
||||
for (x, y) in log_scales.iter().zip(log_vars.iter()) {
|
||||
num += (x - mean_x) * (y - mean_y);
|
||||
den += (x - mean_x).powi(2);
|
||||
}
|
||||
|
||||
let slope = num / den;
|
||||
(slope / 2.0).clamp(0.01, 0.99)
|
||||
}
|
||||
|
||||
/// Market impact function: Impact(Q) ~ Q^δ where δ = 2 - 2H₀
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MarketImpact {
|
||||
/// Impact exponent δ
|
||||
pub delta: f64,
|
||||
/// Scaling constant
|
||||
pub scale: f64,
|
||||
}
|
||||
|
||||
impl MarketImpact {
|
||||
/// Create from unified theory parameter H₀
|
||||
pub fn from_h0(h0: f64, scale: f64) -> Self {
|
||||
Self {
|
||||
delta: 2.0 - 2.0 * h0,
|
||||
scale,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create square-root impact (H₀ = 0.75)
|
||||
pub fn square_root(scale: f64) -> Self {
|
||||
Self {
|
||||
delta: 0.5,
|
||||
scale,
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate market impact for order size Q
|
||||
pub fn impact(&self, q: f64) -> f64 {
|
||||
self.scale * q.abs().powf(self.delta) * q.signum()
|
||||
}
|
||||
|
||||
/// Inverse: order size needed for target impact
|
||||
pub fn order_size(&self, target_impact: f64) -> f64 {
|
||||
(target_impact.abs() / self.scale).powf(1.0 / self.delta) * target_impact.signum()
|
||||
}
|
||||
}
|
||||
|
||||
/// Temporary price impact with decay
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TransientImpact {
|
||||
/// Impact function
|
||||
pub impact: MarketImpact,
|
||||
/// Decay kernel exponent (controls how impact dissipates)
|
||||
pub decay_exponent: f64,
|
||||
}
|
||||
|
||||
impl TransientImpact {
|
||||
pub fn from_h0(h0: f64, scale: f64) -> Self {
|
||||
Self {
|
||||
impact: MarketImpact::from_h0(h0, scale),
|
||||
decay_exponent: 2.0 * h0 - 1.0, // Derived from no-arbitrage
|
||||
}
|
||||
}
|
||||
|
||||
/// Decay kernel G(t) ~ t^{-(2H₀-1)}
|
||||
pub fn decay(&self, t: f64) -> f64 {
|
||||
if t <= 0.0 {
|
||||
return 1.0;
|
||||
}
|
||||
t.powf(-self.decay_exponent)
|
||||
}
|
||||
|
||||
/// Total impact at time t from orders (times, sizes)
|
||||
pub fn total_impact(&self, t: f64, orders: &[(f64, f64)]) -> f64 {
|
||||
let mut total = 0.0;
|
||||
for (order_time, size) in orders {
|
||||
if *order_time < t {
|
||||
let elapsed = t - order_time;
|
||||
total += self.impact.impact(*size) * self.decay(elapsed);
|
||||
}
|
||||
}
|
||||
total
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_unified_theory_params() {
|
||||
let params = UnifiedTheoryParams::new(0.75);
|
||||
|
||||
assert!((params.volume_hurst() - 0.25).abs() < 1e-10);
|
||||
assert!((params.volatility_hurst() - 0.0).abs() < 1e-10);
|
||||
assert!((params.impact_exponent() - 0.5).abs() < 1e-10);
|
||||
assert!(params.is_semimartingale() == false); // H₀ = 0.75 is boundary
|
||||
|
||||
let params_high = UnifiedTheoryParams::new(0.8);
|
||||
assert!(params_high.is_semimartingale());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_market_impact() {
|
||||
let impact = MarketImpact::square_root(1.0);
|
||||
|
||||
// Impact should be proportional to sqrt(Q)
|
||||
let i1 = impact.impact(100.0);
|
||||
let i4 = impact.impact(400.0);
|
||||
|
||||
// i4 / i1 ≈ 2 (since sqrt(400) / sqrt(100) = 2)
|
||||
assert!((i4 / i1 - 2.0).abs() < 0.01);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_order_flow_analysis() {
|
||||
// Generate some synthetic order flow
|
||||
use rand::prelude::*;
|
||||
use rand_distr::Normal;
|
||||
|
||||
let mut rng = StdRng::seed_from_u64(42);
|
||||
let normal = Normal::new(0.0, 1.0).unwrap();
|
||||
|
||||
// Add some persistence
|
||||
let mut flow = vec![0.0; 1000];
|
||||
flow[0] = rng.sample(normal);
|
||||
for i in 1..1000 {
|
||||
flow[i] = 0.3 * flow[i-1] + rng.sample(normal);
|
||||
}
|
||||
|
||||
let analyzer = OrderFlowAnalyzer::new();
|
||||
let metrics = analyzer.analyze_signed_flow(&flow);
|
||||
|
||||
// Hurst should be between 0 and 1
|
||||
assert!(metrics.h0 > 0.0 && metrics.h0 < 1.0);
|
||||
assert!(metrics.h_signed_fbm > 0.0 && metrics.h_signed_fbm < 1.0);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,20 @@
|
||||
//! Python bindings for point processes module
|
||||
//!
|
||||
//! Provides Python access to:
|
||||
//! - Hawkes process simulation (univariate and bivariate)
|
||||
//! - Fractional Brownian motion simulation
|
||||
//! - Mixed fractional Brownian motion
|
||||
//! - Hurst exponent estimation
|
||||
//! - Mittag-Leffler special functions
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::PyDict;
|
||||
use numpy::{PyArray1, PyReadonlyArray1};
|
||||
|
||||
use super::kernels::{ExcitationKernel, PowerLawKernel, ExponentialKernel};
|
||||
use super::hawkes::{HawkesProcess, HawkesProcessConfig, BivariateHawkes};
|
||||
use super::hawkes::{HawkesProcess, BivariateHawkes};
|
||||
use super::mittag_leffler::{mittag_leffler, f_alpha_lambda};
|
||||
use super::mixed_fbm::{FractionalBM, MixedFractionalBM};
|
||||
use super::order_flow::{OrderFlowAnalyzer, UnifiedTheoryParams, MarketImpact};
|
||||
|
||||
/// Simulate a univariate Hawkes process
|
||||
///
|
||||
@@ -184,99 +190,6 @@ pub fn scale_dependent_hurst<'py>(
|
||||
Ok(dict)
|
||||
}
|
||||
|
||||
/// Analyze order flow data using unified theory framework
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `flow` - Signed order flow data (positive = buy, negative = sell)
|
||||
///
|
||||
/// # Returns
|
||||
/// Dictionary with metrics:
|
||||
/// - h0: Estimated H₀ (core flow persistence)
|
||||
/// - h_signed_fbm: Hurst under pure fBM assumption
|
||||
/// - h_signed_mfbm: Hurst under mfBM assumption
|
||||
/// - h_unsigned: Hurst of unsigned volume
|
||||
/// - h_volatility: Implied volatility Hurst (2H₀ - 3/2)
|
||||
/// - impact_exponent: Implied market impact exponent (2 - 2H₀)
|
||||
/// - acf_1: First-order autocorrelation
|
||||
/// - scale_hurst: Scale-dependent Hurst estimates
|
||||
#[pyfunction]
|
||||
pub fn analyze_order_flow<'py>(
|
||||
py: Python<'py>,
|
||||
flow: PyReadonlyArray1<f64>,
|
||||
) -> PyResult<Bound<'py, PyDict>> {
|
||||
let slice = flow.as_slice()?;
|
||||
|
||||
let analyzer = OrderFlowAnalyzer::new();
|
||||
let metrics = analyzer.analyze_signed_flow(slice);
|
||||
|
||||
let dict = PyDict::new_bound(py);
|
||||
dict.set_item("h0", metrics.h0)?;
|
||||
dict.set_item("h_signed_fbm", metrics.h_signed_fbm)?;
|
||||
dict.set_item("h_signed_mfbm", metrics.h_signed_mfbm)?;
|
||||
dict.set_item("h_unsigned", metrics.h_unsigned)?;
|
||||
dict.set_item("h_volatility", metrics.h_volatility)?;
|
||||
dict.set_item("impact_exponent", metrics.impact_exponent)?;
|
||||
dict.set_item("total_signed", metrics.total_signed)?;
|
||||
dict.set_item("total_unsigned", metrics.total_unsigned)?;
|
||||
dict.set_item("acf_1", metrics.acf_1)?;
|
||||
|
||||
// Scale-dependent Hurst as nested dict
|
||||
let scale_dict = PyDict::new_bound(py);
|
||||
for (scale, h) in metrics.scale_hurst {
|
||||
scale_dict.set_item(scale, h)?;
|
||||
}
|
||||
dict.set_item("scale_hurst", scale_dict)?;
|
||||
|
||||
Ok(dict)
|
||||
}
|
||||
|
||||
/// Get unified theory derived quantities from H₀
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `h0` - Hurst index of signed order flow (typically ~0.75)
|
||||
///
|
||||
/// # Returns
|
||||
/// Dictionary with derived parameters:
|
||||
/// - h0: Input H₀
|
||||
/// - alpha0: Tail exponent α₀ = H₀/2
|
||||
/// - h_volume: Volume Hurst H₁ = H₀ - 0.5
|
||||
/// - h_volatility: Volatility Hurst = 2H₀ - 1.5
|
||||
/// - impact_exponent: Market impact exponent δ = 2 - 2H₀
|
||||
/// - is_semimartingale: Whether mfBM is a semimartingale (H₀ > 3/4)
|
||||
#[pyfunction]
|
||||
pub fn unified_theory_params<'py>(
|
||||
py: Python<'py>,
|
||||
h0: f64,
|
||||
) -> PyResult<Bound<'py, PyDict>> {
|
||||
let params = UnifiedTheoryParams::new(h0);
|
||||
|
||||
let dict = PyDict::new_bound(py);
|
||||
dict.set_item("h0", params.h0)?;
|
||||
dict.set_item("alpha0", params.alpha0())?;
|
||||
dict.set_item("h_volume", params.volume_hurst())?;
|
||||
dict.set_item("h_volatility", params.volatility_hurst())?;
|
||||
dict.set_item("impact_exponent", params.impact_exponent())?;
|
||||
dict.set_item("is_semimartingale", params.is_semimartingale())?;
|
||||
|
||||
Ok(dict)
|
||||
}
|
||||
|
||||
/// Compute market impact for given order size
|
||||
///
|
||||
/// Impact(Q) = scale * |Q|^δ * sign(Q)
|
||||
/// where δ = 2 - 2*H₀
|
||||
#[pyfunction]
|
||||
#[pyo3(signature = (q, h0=0.75, scale=1.0))]
|
||||
pub fn market_impact<'py>(
|
||||
_py: Python<'py>,
|
||||
q: f64,
|
||||
h0: f64,
|
||||
scale: f64,
|
||||
) -> PyResult<f64> {
|
||||
let impact = MarketImpact::from_h0(h0, scale);
|
||||
Ok(impact.impact(q))
|
||||
}
|
||||
|
||||
/// Register all point process functions to PyO3 module
|
||||
pub fn register_python_functions(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
// Hawkes processes
|
||||
@@ -293,10 +206,5 @@ pub fn register_python_functions(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_function(wrap_pyfunction!(estimate_hurst, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(scale_dependent_hurst, m)?)?;
|
||||
|
||||
// Order flow analysis
|
||||
m.add_function(wrap_pyfunction!(analyze_order_flow, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(unified_theory_params, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(market_impact, m)?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user