nt-napi-bindings 1.0.0

Node.js N-API bindings for Neural Trader - High-performance TypeScript/JavaScript interface
Documentation
//! Strategy 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;

/// Trading signal from a strategy
#[napi(object)]
pub struct Signal {
    pub id: String,
    pub strategy_id: String,
    pub symbol: String,
    pub direction: String,  // "long", "short", "close"
    pub confidence: f64,    // 0.0-1.0
    pub entry_price: Option<f64>,
    pub stop_loss: Option<f64>,
    pub take_profit: Option<f64>,
    pub reasoning: String,
    pub timestamp_ns: i64,
}

/// Strategy configuration
#[napi(object)]
pub struct StrategyConfig {
    pub name: String,
    pub symbols: Vec<String>,
    pub parameters: String,  // JSON string
}

/// Strategy runner that manages multiple strategies
#[napi]
pub struct StrategyRunner {
    // In a real implementation, this would use the actual strategy types
    // For now, we'll use a placeholder structure
    strategies: Arc<Mutex<Vec<String>>>,
}

#[napi]
impl StrategyRunner {
    /// Create a new strategy runner
    #[napi(constructor)]
    pub fn new() -> Self {
        Self {
            strategies: Arc::new(Mutex::new(Vec::new())),
        }
    }

    /// Add a momentum strategy
    #[napi]
    pub async fn add_momentum_strategy(&self, config: StrategyConfig) -> Result<String> {
        let strategy_id = format!("momentum-{}", generate_uuid());

        let mut strategies = self.strategies.lock().await;
        strategies.push(strategy_id.clone());

        tracing::info!("Added momentum strategy: {} with config: {:?}", strategy_id, config.name);

        Ok(strategy_id)
    }

    /// Add a mean reversion strategy
    #[napi]
    pub async fn add_mean_reversion_strategy(&self, _config: StrategyConfig) -> Result<String> {
        let strategy_id = format!("mean-reversion-{}", generate_uuid());

        let mut strategies = self.strategies.lock().await;
        strategies.push(strategy_id.clone());

        tracing::info!("Added mean reversion strategy: {}", strategy_id);

        Ok(strategy_id)
    }

    /// Add an arbitrage strategy
    #[napi]
    pub async fn add_arbitrage_strategy(&self, _config: StrategyConfig) -> Result<String> {
        let strategy_id = format!("arbitrage-{}", generate_uuid());

        let mut strategies = self.strategies.lock().await;
        strategies.push(strategy_id.clone());

        tracing::info!("Added arbitrage strategy: {}", strategy_id);

        Ok(strategy_id)
    }

    /// Generate signals from all strategies
    #[napi]
    pub async fn generate_signals(&self) -> Result<Vec<Signal>> {
        let strategies = self.strategies.lock().await;
        let mut all_signals = Vec::new();

        // In a real implementation, this would call actual strategy logic
        // For now, return empty signals
        for strategy_id in strategies.iter() {
            tracing::debug!("Generating signals for strategy: {}", strategy_id);
        }

        Ok(all_signals)
    }

    /// Subscribe to signals with a callback
    #[napi]
    pub fn subscribe_signals(&self, callback: JsFunction) -> Result<SubscriptionHandle> {
        let _tsfn: ThreadsafeFunction<Signal, ErrorStrategy::CalleeHandled> =
            callback.create_threadsafe_function(0, |ctx| Ok(vec![ctx.value]))?;

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

                // In a real implementation, check for signals
                let _strategies = strategies.lock().await;

                // For now, just sleep
            }
        });

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

    /// Get list of active strategies
    #[napi]
    pub async fn list_strategies(&self) -> Result<Vec<String>> {
        let strategies = self.strategies.lock().await;
        Ok(strategies.clone())
    }

    /// Remove a strategy by ID
    #[napi]
    pub async fn remove_strategy(&self, strategy_id: String) -> Result<bool> {
        let mut strategies = self.strategies.lock().await;
        if let Some(pos) = strategies.iter().position(|id| id == &strategy_id) {
            strategies.remove(pos);
            tracing::info!("Removed strategy: {}", strategy_id);
            Ok(true)
        } else {
            Ok(false)
        }
    }
}

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

#[napi]
impl SubscriptionHandle {
    /// Unsubscribe from signals
    #[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 signals");
        }
        Ok(())
    }
}

// UUID generation helper
fn generate_uuid() -> String {
    use std::time::{SystemTime, UNIX_EPOCH};
    let nanos = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_nanos();
    format!("{:x}", nanos)
}