nt-napi-bindings 1.0.0

Node.js N-API bindings for Neural Trader - High-performance TypeScript/JavaScript interface
Documentation
//! Order execution bindings for Node.js

use napi::bindgen_prelude::*;
use napi::threadsafe_function::{ErrorStrategy, ThreadsafeFunction};
use napi_derive::napi;
use std::sync::Arc;
use tokio::sync::Mutex;

/// Trade order request
#[napi(object)]
pub struct TradeOrder {
    pub symbol: String,
    pub side: String,           // "BUY", "SELL"
    pub quantity: u32,
    pub order_type: String,     // "MARKET", "LIMIT", "STOP"
    pub price: Option<f64>,
    pub time_in_force: Option<String>, // "GTC", "DAY", "IOC", "FOK"
}

/// Order execution result
#[napi(object)]
pub struct ExecutionResult {
    pub order_id: String,
    pub status: String,         // "FILLED", "PARTIAL", "REJECTED", "PENDING"
    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,
}

/// Execution engine configuration
#[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>,
}

/// Execution statistics
#[napi(object)]
pub struct ExecutionStats {
    pub orders_processed: i64,
    pub avg_latency_ns: i64,
    pub buffer_utilization: f64,
    pub uptime_seconds: f64,
}

/// Ultra-low latency execution engine
#[napi]
pub struct ExecutionEngine {
    config: ExecutionConfig,
    orders_processed: Arc<Mutex<i64>>,
    start_time: std::time::Instant,
}

#[napi]
impl ExecutionEngine {
    /// Create a new execution engine
    #[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(),
        })
    }

    /// Start the execution engine
    #[napi]
    pub async fn start(&self) -> Result<()> {
        tracing::info!("Starting execution engine...");

        // In a real implementation:
        // - Connect to WebSocket
        // - Initialize order buffers
        // - Start background processing tasks

        tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
        tracing::info!("Execution engine started");

        Ok(())
    }

    /// Stop the execution engine gracefully
    #[napi]
    pub async fn stop(&self) -> Result<()> {
        tracing::info!("Stopping execution engine...");

        // In a real implementation:
        // - Cancel all pending orders
        // - Close WebSocket connection
        // - Flush buffers

        tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
        tracing::info!("Execution engine stopped");

        Ok(())
    }

    /// Submit a trade order
    #[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
        );

        // Validation phase
        let validation_start = std::time::Instant::now();
        self.validate_order(&order)?;
        let validation_time_ns = validation_start.elapsed().as_nanos() as i64;

        // Execution phase
        let execution_start = std::time::Instant::now();

        // In a real implementation:
        // - Send order to broker API
        // - Wait for fill confirmation
        // - Handle partial fills

        // Simulate execution
        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;

        // Increment counter
        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)
    }

    /// Subscribe to market data stream
    #[napi]
    pub async fn subscribe_market_data(
        &self,
        _symbols: Vec<String>,
        callback: JsFunction,
    ) -> Result<SubscriptionHandle> {
        tracing::info!("Subscribing to market data");

        // Create threadsafe function
        let _tsfn: ThreadsafeFunction<String, ErrorStrategy::CalleeHandled> =
            callback.create_threadsafe_function(0, |ctx| Ok(vec![ctx.value]))?;

        // Spawn background task
        let handle = tokio::spawn(async move {
            loop {
                tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;

                // In a real implementation, forward market data from WebSocket
            }
        });

        Ok(SubscriptionHandle {
            handle: Arc::new(Mutex::new(Some(handle))),
        })
    }

    /// Get current engine statistics
    #[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 }, // 50ms average
            buffer_utilization: 0.35, // 35% buffer usage
            uptime_seconds: uptime,
        })
    }

    /// Validate order before submission
    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(())
    }
}

/// Subscription handle for cleanup
#[napi]
pub struct SubscriptionHandle {
    handle: Arc<Mutex<Option<tokio::task::JoinHandle<()>>>>,
}

#[napi]
impl SubscriptionHandle {
    /// Unsubscribe from market data
    #[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(())
    }
}