neural_trader/
strategy.rs

1//! Strategy bindings for Node.js
2
3use napi::bindgen_prelude::*;
4use napi::threadsafe_function::{ErrorStrategy, ThreadsafeFunction};
5use napi_derive::napi;
6use std::sync::Arc;
7use tokio::sync::Mutex;
8
9/// Trading signal from a strategy
10#[napi(object)]
11pub struct Signal {
12    pub id: String,
13    pub strategy_id: String,
14    pub symbol: String,
15    pub direction: String,  // "long", "short", "close"
16    pub confidence: f64,    // 0.0-1.0
17    pub entry_price: Option<f64>,
18    pub stop_loss: Option<f64>,
19    pub take_profit: Option<f64>,
20    pub reasoning: String,
21    pub timestamp_ns: i64,
22}
23
24/// Strategy configuration
25#[napi(object)]
26pub struct StrategyConfig {
27    pub name: String,
28    pub symbols: Vec<String>,
29    pub parameters: String,  // JSON string
30}
31
32/// Strategy runner that manages multiple strategies
33#[napi]
34pub struct StrategyRunner {
35    // In a real implementation, this would use the actual strategy types
36    // For now, we'll use a placeholder structure
37    strategies: Arc<Mutex<Vec<String>>>,
38}
39
40#[napi]
41impl StrategyRunner {
42    /// Create a new strategy runner
43    #[napi(constructor)]
44    pub fn new() -> Self {
45        Self {
46            strategies: Arc::new(Mutex::new(Vec::new())),
47        }
48    }
49
50    /// Add a momentum strategy
51    #[napi]
52    pub async fn add_momentum_strategy(&self, config: StrategyConfig) -> Result<String> {
53        let strategy_id = format!("momentum-{}", generate_uuid());
54
55        let mut strategies = self.strategies.lock().await;
56        strategies.push(strategy_id.clone());
57
58        tracing::info!("Added momentum strategy: {} with config: {:?}", strategy_id, config.name);
59
60        Ok(strategy_id)
61    }
62
63    /// Add a mean reversion strategy
64    #[napi]
65    pub async fn add_mean_reversion_strategy(&self, _config: StrategyConfig) -> Result<String> {
66        let strategy_id = format!("mean-reversion-{}", generate_uuid());
67
68        let mut strategies = self.strategies.lock().await;
69        strategies.push(strategy_id.clone());
70
71        tracing::info!("Added mean reversion strategy: {}", strategy_id);
72
73        Ok(strategy_id)
74    }
75
76    /// Add an arbitrage strategy
77    #[napi]
78    pub async fn add_arbitrage_strategy(&self, _config: StrategyConfig) -> Result<String> {
79        let strategy_id = format!("arbitrage-{}", generate_uuid());
80
81        let mut strategies = self.strategies.lock().await;
82        strategies.push(strategy_id.clone());
83
84        tracing::info!("Added arbitrage strategy: {}", strategy_id);
85
86        Ok(strategy_id)
87    }
88
89    /// Generate signals from all strategies
90    #[napi]
91    pub async fn generate_signals(&self) -> Result<Vec<Signal>> {
92        let strategies = self.strategies.lock().await;
93        let mut all_signals = Vec::new();
94
95        // In a real implementation, this would call actual strategy logic
96        // For now, return empty signals
97        for strategy_id in strategies.iter() {
98            tracing::debug!("Generating signals for strategy: {}", strategy_id);
99        }
100
101        Ok(all_signals)
102    }
103
104    /// Subscribe to signals with a callback
105    #[napi]
106    pub fn subscribe_signals(&self, callback: JsFunction) -> Result<SubscriptionHandle> {
107        let _tsfn: ThreadsafeFunction<Signal, ErrorStrategy::CalleeHandled> =
108            callback.create_threadsafe_function(0, |ctx| Ok(vec![ctx.value]))?;
109
110        // Spawn background task for signal generation
111        let strategies = self.strategies.clone();
112        let handle = tokio::spawn(async move {
113            loop {
114                tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
115
116                // In a real implementation, check for signals
117                let _strategies = strategies.lock().await;
118
119                // For now, just sleep
120            }
121        });
122
123        Ok(SubscriptionHandle {
124            handle: Arc::new(Mutex::new(Some(handle))),
125        })
126    }
127
128    /// Get list of active strategies
129    #[napi]
130    pub async fn list_strategies(&self) -> Result<Vec<String>> {
131        let strategies = self.strategies.lock().await;
132        Ok(strategies.clone())
133    }
134
135    /// Remove a strategy by ID
136    #[napi]
137    pub async fn remove_strategy(&self, strategy_id: String) -> Result<bool> {
138        let mut strategies = self.strategies.lock().await;
139        if let Some(pos) = strategies.iter().position(|id| id == &strategy_id) {
140            strategies.remove(pos);
141            tracing::info!("Removed strategy: {}", strategy_id);
142            Ok(true)
143        } else {
144            Ok(false)
145        }
146    }
147}
148
149/// Subscription handle for cleanup
150#[napi]
151pub struct SubscriptionHandle {
152    handle: Arc<Mutex<Option<tokio::task::JoinHandle<()>>>>,
153}
154
155#[napi]
156impl SubscriptionHandle {
157    /// Unsubscribe from signals
158    #[napi]
159    pub async fn unsubscribe(&self) -> Result<()> {
160        let mut guard = self.handle.lock().await;
161        if let Some(handle) = guard.take() {
162            handle.abort();
163            tracing::info!("Unsubscribed from signals");
164        }
165        Ok(())
166    }
167}
168
169// UUID generation helper
170fn generate_uuid() -> String {
171    use std::time::{SystemTime, UNIX_EPOCH};
172    let nanos = SystemTime::now()
173        .duration_since(UNIX_EPOCH)
174        .unwrap()
175        .as_nanos();
176    format!("{:x}", nanos)
177}