refactor: restructure repo — crates/, bin/, testing/ layout
Move 17 library crates into crates/, CLI binary into bin/fxt, consolidate 10 test crates into testing/, split config crate from deployment config files. Root directory reduced from 38+ to ~17 directories. All Cargo.toml paths and build.rs proto refs updated. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
127
crates/ml/src/microstructure/kyle_lambda.rs
Normal file
127
crates/ml/src/microstructure/kyle_lambda.rs
Normal file
@@ -0,0 +1,127 @@
|
||||
//! # Kyle's Lambda Estimator
|
||||
//!
|
||||
//! Implementation of Kyle's Lambda for measuring price impact and
|
||||
//! information asymmetry in financial markets.
|
||||
//!
|
||||
//! ## Algorithm
|
||||
//!
|
||||
//! Kyle's Lambda (λ) measures the price impact per unit of signed order flow:
|
||||
//! - Returns = λ × SignedOrderFlow + ε
|
||||
//! - λ is estimated via regression of returns on signed square-root dollar volume
|
||||
//! - Higher λ indicates greater price impact (lower liquidity)
|
||||
//!
|
||||
//! ## Performance
|
||||
//!
|
||||
//! - Target latency: <25μs per calculation
|
||||
//! - Rolling regression with fixed-point arithmetic
|
||||
//! - Efficient covariance calculation updates
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::atomic::{AtomicI64, AtomicU64, Ordering};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::*;
|
||||
use super::{
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_kyle_lambda_estimator_creation() {
|
||||
let estimator = KyleLambdaEstimator::default();
|
||||
assert_eq!(estimator.get_lambda(), 0.0);
|
||||
assert_eq!(estimator.get_interval_count(), 0);
|
||||
assert_eq!(estimator.get_r_squared(), 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trading_interval() {
|
||||
let mut interval = TradingInterval::new(0, 1000000, 2000000);
|
||||
|
||||
let update = MarketDataUpdate {
|
||||
timestamp: 1500000,
|
||||
symbol: "AAPL".to_owned(),
|
||||
price: 150000,
|
||||
volume: 1000,
|
||||
bid: 149000,
|
||||
ask: 151000,
|
||||
bid_size: 100,
|
||||
ask_size: 100,
|
||||
direction: Some(TradeDirection::Buy),
|
||||
};
|
||||
|
||||
interval.add_trade(&update);
|
||||
|
||||
assert_eq!(interval.trade_count, 1);
|
||||
assert_eq!(interval.open_price, 150000);
|
||||
assert_eq!(interval.close_price, 150000);
|
||||
assert!(interval.signed_sqrt_dollar_volume > 0); // Buy trade
|
||||
|
||||
interval.finalize();
|
||||
assert!(interval.is_valid());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lambda_calculation() {
|
||||
let config = KyleLambdaConfig {
|
||||
interval_duration_ns: 1000000, // 1ms for testing
|
||||
min_trades_per_interval: 1,
|
||||
regression_window: 5,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut estimator = KyleLambdaEstimator::new(config);
|
||||
|
||||
// Add trades with price impact pattern
|
||||
for i in 0..20 {
|
||||
let price_impact = if i % 2 == 0 { 100 } else { -100 };
|
||||
let direction = if i % 2 == 0 { TradeDirection::Buy } else { TradeDirection::Sell };
|
||||
|
||||
let update = MarketDataUpdate {
|
||||
timestamp: (i * 2000000) as u64, // 2ms intervals
|
||||
symbol: "AAPL".to_owned(),
|
||||
price: 150000 + price_impact,
|
||||
volume: 1000,
|
||||
bid: 149000,
|
||||
ask: 151000,
|
||||
bid_size: 100,
|
||||
ask_size: 100,
|
||||
direction: Some(direction),
|
||||
};
|
||||
|
||||
estimator.update(&update)?;
|
||||
}
|
||||
|
||||
// Should have calculated lambda
|
||||
let result = estimator.get_result();
|
||||
assert!(result.interval_count > 0);
|
||||
|
||||
// Lambda should be non-zero if there's a price impact pattern
|
||||
// (exact value depends on the specific pattern)
|
||||
println!("Lambda: {}, R²: {}", result.lambda, result.r_squared);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_information_asymmetry() {
|
||||
let mut estimator = KyleLambdaEstimator::default();
|
||||
|
||||
// Add persistent positive returns (trend)
|
||||
for i in 0..10 {
|
||||
let update = MarketDataUpdate {
|
||||
timestamp: (i * 300_000_000_000) as u64, // 5 min intervals
|
||||
symbol: "AAPL".to_owned(),
|
||||
price: 150000 + (i * 100), // Trending up
|
||||
volume: 1000,
|
||||
bid: 149000,
|
||||
ask: 151000,
|
||||
bid_size: 100,
|
||||
ask_size: 100,
|
||||
direction: Some(TradeDirection::Buy),
|
||||
};
|
||||
|
||||
estimator.update(&update)?;
|
||||
}
|
||||
|
||||
let info_asymmetry = estimator.get_information_asymmetry();
|
||||
assert!(info_asymmetry >= 0.0); // Should detect some persistence
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user