use napi::bindgen_prelude::*;
use napi::threadsafe_function::{ErrorStrategy, ThreadsafeFunction};
use napi_derive::napi;
use std::sync::Arc;
use tokio::sync::Mutex;
#[napi(object)]
pub struct TradeOrder {
pub symbol: String,
pub side: String, pub quantity: u32,
pub order_type: String, pub price: Option<f64>,
pub time_in_force: Option<String>, }
#[napi(object)]
pub struct ExecutionResult {
pub order_id: String,
pub status: String, pub filled_quantity: u32,
pub avg_price: f64,
pub total_latency_ns: i64,
pub validation_time_ns: i64,
pub execution_time_ns: i64,
pub timestamp_ns: i64,
}
#[napi(object)]
pub struct ExecutionConfig {
pub websocket_url: String,
pub api_key: String,
pub secret_key: String,
pub buffer_size: Option<u32>,
pub max_latency_ms: Option<u32>,
}
#[napi(object)]
pub struct ExecutionStats {
pub orders_processed: i64,
pub avg_latency_ns: i64,
pub buffer_utilization: f64,
pub uptime_seconds: f64,
}
#[napi]
pub struct ExecutionEngine {
config: ExecutionConfig,
orders_processed: Arc<Mutex<i64>>,
start_time: std::time::Instant,
}
#[napi]
impl ExecutionEngine {
#[napi(constructor)]
pub fn new(config: ExecutionConfig) -> Result<Self> {
tracing::info!("Creating execution engine with buffer size: {:?}", config.buffer_size);
Ok(Self {
config,
orders_processed: Arc::new(Mutex::new(0)),
start_time: std::time::Instant::now(),
})
}
#[napi]
pub async fn start(&self) -> Result<()> {
tracing::info!("Starting execution engine...");
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
tracing::info!("Execution engine started");
Ok(())
}
#[napi]
pub async fn stop(&self) -> Result<()> {
tracing::info!("Stopping execution engine...");
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
tracing::info!("Execution engine stopped");
Ok(())
}
#[napi]
pub async fn submit_order(&self, order: TradeOrder) -> Result<ExecutionResult> {
let start = std::time::Instant::now();
tracing::info!(
"Submitting order: {} {} {} @ {:?}",
order.side,
order.quantity,
order.symbol,
order.price
);
let validation_start = std::time::Instant::now();
self.validate_order(&order)?;
let validation_time_ns = validation_start.elapsed().as_nanos() as i64;
let execution_start = std::time::Instant::now();
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
let execution_time_ns = execution_start.elapsed().as_nanos() as i64;
let total_latency_ns = start.elapsed().as_nanos() as i64;
let mut count = self.orders_processed.lock().await;
*count += 1;
let order_id = format!("ORD-{}", *count);
let result = ExecutionResult {
order_id: order_id.clone(),
status: "FILLED".to_string(),
filled_quantity: order.quantity,
avg_price: order.price.unwrap_or(100.0),
total_latency_ns,
validation_time_ns,
execution_time_ns,
timestamp_ns: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos() as i64,
};
tracing::info!(
"Order executed: {} in {}ms",
order_id,
total_latency_ns as f64 / 1_000_000.0
);
Ok(result)
}
#[napi]
pub async fn subscribe_market_data(
&self,
_symbols: Vec<String>,
callback: JsFunction,
) -> Result<SubscriptionHandle> {
tracing::info!("Subscribing to market data");
let _tsfn: ThreadsafeFunction<String, ErrorStrategy::CalleeHandled> =
callback.create_threadsafe_function(0, |ctx| Ok(vec![ctx.value]))?;
let handle = tokio::spawn(async move {
loop {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
}
});
Ok(SubscriptionHandle {
handle: Arc::new(Mutex::new(Some(handle))),
})
}
#[napi]
pub async fn get_stats(&self) -> Result<ExecutionStats> {
let orders_processed = *self.orders_processed.lock().await;
let uptime = self.start_time.elapsed().as_secs_f64();
Ok(ExecutionStats {
orders_processed,
avg_latency_ns: if orders_processed > 0 { 50_000_000 } else { 0 }, buffer_utilization: 0.35, uptime_seconds: uptime,
})
}
fn validate_order(&self, order: &TradeOrder) -> Result<()> {
if order.symbol.is_empty() {
return Err(Error::from_reason("Symbol cannot be empty"));
}
if order.quantity == 0 {
return Err(Error::from_reason("Quantity must be greater than 0"));
}
if order.side != "BUY" && order.side != "SELL" {
return Err(Error::from_reason("Side must be BUY or SELL"));
}
Ok(())
}
}
#[napi]
pub struct SubscriptionHandle {
handle: Arc<Mutex<Option<tokio::task::JoinHandle<()>>>>,
}
#[napi]
impl SubscriptionHandle {
#[napi]
pub async fn unsubscribe(&self) -> Result<()> {
let mut guard = self.handle.lock().await;
if let Some(handle) = guard.take() {
handle.abort();
tracing::info!("Unsubscribed from market data");
}
Ok(())
}
}