use napi::bindgen_prelude::*;
use napi_derive::napi;
use std::sync::Arc;
use nt_core::prelude::*;
pub mod broker;
pub mod neural;
pub mod risk;
pub mod backtest;
pub mod market_data;
pub mod strategy;
pub mod portfolio;
type NapiResult<T> = napi::Result<T>;
#[napi(object)]
pub struct JsBar {
pub symbol: String,
pub timestamp: String,
pub open: String,
pub high: String,
pub low: String,
pub close: String,
pub volume: String,
}
impl From<Bar> for JsBar {
fn from(bar: Bar) -> Self {
Self {
symbol: bar.symbol.to_string(),
timestamp: bar.timestamp.to_rfc3339(),
open: bar.open.to_string(),
high: bar.high.to_string(),
low: bar.low.to_string(),
close: bar.close.to_string(),
volume: bar.volume.to_string(),
}
}
}
#[napi(object)]
pub struct JsSignal {
pub id: String,
pub strategy_id: String,
pub symbol: String,
pub direction: String,
pub confidence: f64,
pub entry_price: Option<String>,
pub stop_loss: Option<String>,
pub take_profit: Option<String>,
pub quantity: Option<String>,
pub reasoning: String,
pub timestamp: String,
}
impl From<Signal> for JsSignal {
fn from(signal: Signal) -> Self {
Self {
id: signal.id.to_string(),
strategy_id: signal.strategy_id,
symbol: signal.symbol.to_string(),
direction: signal.direction.to_string(),
confidence: signal.confidence,
entry_price: signal.entry_price.map(|p| p.to_string()),
stop_loss: signal.stop_loss.map(|p| p.to_string()),
take_profit: signal.take_profit.map(|p| p.to_string()),
quantity: signal.quantity.map(|q| q.to_string()),
reasoning: signal.reasoning,
timestamp: signal.timestamp.to_rfc3339(),
}
}
}
#[napi(object)]
pub struct JsOrder {
pub id: String,
pub symbol: String,
pub side: String,
pub order_type: String,
pub quantity: String,
pub limit_price: Option<String>,
pub stop_price: Option<String>,
pub time_in_force: String,
}
#[napi(object)]
pub struct JsPosition {
pub symbol: String,
pub quantity: String,
pub avg_entry_price: String,
pub current_price: String,
pub unrealized_pnl: String,
pub side: String,
pub market_value: String,
}
impl From<Position> for JsPosition {
fn from(pos: Position) -> Self {
Self {
symbol: pos.symbol.to_string(),
quantity: pos.quantity.to_string(),
avg_entry_price: pos.avg_entry_price.to_string(),
current_price: pos.current_price.to_string(),
unrealized_pnl: pos.unrealized_pnl.to_string(),
side: pos.side.to_string(),
market_value: pos.market_value().to_string(),
}
}
}
#[napi(object)]
pub struct JsConfig {
pub api_key: Option<String>,
pub api_secret: Option<String>,
pub base_url: Option<String>,
pub paper_trading: bool,
}
fn to_napi_error(err: TradingError) -> Error {
match err {
TradingError::MarketData { message, .. } => {
Error::from_reason(format!("Market data error: {}", message))
}
TradingError::Strategy {
strategy_id,
message,
..
} => Error::from_reason(format!("Strategy error ({}): {}", strategy_id, message)),
TradingError::Execution {
message, order_id, ..
} => {
let msg = if let Some(id) = order_id {
format!("Execution error (order {}): {}", id, message)
} else {
format!("Execution error: {}", message)
};
Error::from_reason(msg)
}
TradingError::RiskLimit {
message,
violation_type,
} => Error::from_reason(format!("Risk violation ({:?}): {}", violation_type, message)),
TradingError::Validation { message } => {
Error::from_reason(format!("Validation error: {}", message))
}
TradingError::NotFound {
resource_type,
resource_id,
} => Error::from_reason(format!("Not found: {} '{}'", resource_type, resource_id)),
TradingError::Timeout {
operation,
timeout_ms,
} => Error::from_reason(format!(
"Operation '{}' timed out after {}ms",
operation, timeout_ms
)),
_ => Error::from_reason(err.to_string()),
}
}
#[napi]
pub struct NeuralTrader {
_config: Arc<JsConfig>,
}
#[napi]
impl NeuralTrader {
#[napi(constructor)]
pub fn new(config: JsConfig) -> Self {
Self {
_config: Arc::new(config),
}
}
#[napi]
pub async fn start(&self) -> NapiResult<()> {
Ok(())
}
#[napi]
pub async fn stop(&self) -> NapiResult<()> {
Ok(())
}
#[napi]
pub async fn get_positions(&self) -> NapiResult<Vec<JsPosition>> {
Ok(vec![])
}
#[napi]
pub async fn place_order(&self, _order: JsOrder) -> NapiResult<String> {
Ok("order-id-placeholder".to_string())
}
#[napi]
pub async fn get_balance(&self) -> NapiResult<String> {
Ok("0.00".to_string())
}
#[napi]
pub async fn get_equity(&self) -> NapiResult<String> {
Ok("0.00".to_string())
}
}
#[napi]
pub async fn fetch_market_data(
_symbol: String,
_start: String,
_end: String,
_timeframe: String,
) -> NapiResult<Vec<JsBar>> {
Ok(vec![])
}
#[napi]
pub async fn calculate_indicator(
_bars: Vec<JsBar>,
_indicator: String,
_params: String,
) -> NapiResult<Vec<f64>> {
Ok(vec![])
}
#[napi]
pub fn encode_bars_to_buffer(_bars: Vec<JsBar>) -> NapiResult<Buffer> {
Ok(Buffer::from(vec![]))
}
#[napi]
pub fn decode_bars_from_buffer(_buffer: Buffer) -> NapiResult<Vec<JsBar>> {
Ok(vec![])
}
#[napi]
pub fn init_runtime(num_threads: Option<u32>) -> NapiResult<()> {
let threads = num_threads.unwrap_or_else(|| num_cpus::get() as u32);
tracing::info!("Initializing tokio runtime with {} threads", threads);
Ok(())
}
#[napi(object)]
pub struct VersionInfo {
pub rust_core: String,
pub napi_bindings: String,
pub rust_compiler: String,
}
#[napi]
pub fn get_version_info() -> NapiResult<VersionInfo> {
Ok(VersionInfo {
rust_core: env!("CARGO_PKG_VERSION").to_string(),
napi_bindings: env!("CARGO_PKG_VERSION").to_string(),
rust_compiler: rustc_version_runtime::version().to_string(),
})
}