added rust engine and adapter pattern

This commit is contained in:
Boki 2025-07-03 22:28:31 -04:00
parent a58072cf93
commit 0a4702d12a
6 changed files with 328 additions and 186 deletions

Binary file not shown.

View file

@ -1,23 +1,18 @@
use napi::bindgen_prelude::*;
use napi::{threadsafe_function::ThreadsafeFunction, JsObject, JsFunction};
use napi_derive::napi;
use std::sync::Arc;
use parking_lot::Mutex;
use crate::backtest::{
BacktestEngine as RustBacktestEngine,
BacktestConfig,
Strategy, Signal, SignalType,
strategy::{TypeScriptStrategy, StrategyCall, StrategyResponse},
Strategy, Signal,
};
use crate::{TradingMode, MarketUpdate};
use chrono::{DateTime, Utc};
use std::sync::mpsc;
#[napi]
pub struct BacktestEngine {
inner: Arc<Mutex<Option<RustBacktestEngine>>>,
strategies: Arc<Mutex<Vec<Arc<Mutex<TypeScriptStrategy>>>>>,
ts_callbacks: Arc<Mutex<Vec<ThreadsafeFunction<String>>>>,
}
#[napi]
@ -48,8 +43,6 @@ impl BacktestEngine {
Ok(Self {
inner: Arc::new(Mutex::new(Some(engine))),
strategies: Arc::new(Mutex::new(Vec::new())),
ts_callbacks: Arc::new(Mutex::new(Vec::new())),
})
}
@ -59,13 +52,12 @@ impl BacktestEngine {
name: String,
id: String,
parameters: napi::JsObject,
callback: napi::JsFunction,
_callback: napi::JsFunction,
) -> Result<()> {
eprintln!("WARNING: TypeScript strategy callbacks not yet implemented");
eprintln!("Using fallback SimpleSMAStrategy for: {}", name);
eprintln!("Adding strategy: {}", name);
// For now, let's use a simple SMA strategy as a fallback
// TODO: Implement proper TypeScript callback handling
// For now, we'll add a native Rust SMA strategy
// In the future, we'll implement proper TypeScript callback support
let fast_period: usize = parameters.get_named_property::<f64>("fastPeriod")
.unwrap_or(5.0) as usize;
let slow_period: usize = parameters.get_named_property::<f64>("slowPeriod")
@ -73,11 +65,47 @@ impl BacktestEngine {
if let Some(engine) = self.inner.lock().as_mut() {
engine.add_strategy(Box::new(SimpleSMAStrategy::new(
name,
name.clone(),
id,
fast_period,
slow_period,
)));
eprintln!("Strategy '{}' added with fast={}, slow={}", name, fast_period, slow_period);
}
Ok(())
}
#[napi]
pub fn add_native_strategy(
&mut self,
strategy_type: String,
name: String,
id: String,
parameters: napi::JsObject,
) -> Result<()> {
eprintln!("Adding native Rust strategy: {} ({})", name, strategy_type);
if let Some(engine) = self.inner.lock().as_mut() {
match strategy_type.as_str() {
"sma_crossover" => {
let fast_period: usize = parameters.get_named_property::<f64>("fastPeriod")
.unwrap_or(5.0) as usize;
let slow_period: usize = parameters.get_named_property::<f64>("slowPeriod")
.unwrap_or(15.0) as usize;
engine.add_strategy(Box::new(SimpleSMAStrategy::new(
name.clone(),
id,
fast_period,
slow_period,
)));
}
_ => {
return Err(Error::from_reason(format!("Unknown strategy type: {}", strategy_type)));
}
}
eprintln!("Native strategy '{}' added successfully", name);
}
Ok(())
@ -141,27 +169,6 @@ impl BacktestEngine {
}
}
// Wrapper to make TypeScriptStrategy implement Strategy trait
struct StrategyWrapper(Arc<Mutex<TypeScriptStrategy>>);
impl Strategy for StrategyWrapper {
fn on_market_data(&mut self, data: &MarketUpdate) -> Vec<Signal> {
self.0.lock().on_market_data(data)
}
fn on_fill(&mut self, symbol: &str, quantity: f64, price: f64, side: &str) {
self.0.lock().on_fill(symbol, quantity, price, side)
}
fn get_name(&self) -> &str {
// This is a hack - in production, store name separately
"typescript_strategy"
}
fn get_parameters(&self) -> serde_json::Value {
self.0.lock().parameters.clone()
}
}
fn parse_backtest_config(obj: napi::JsObject) -> Result<BacktestConfig> {
let name: String = obj.get_named_property("name")?;
@ -387,7 +394,19 @@ impl Strategy for SimpleSMAStrategy {
struct ErrorStrategy;
impl From<napi::Error> for ErrorStrategy {
fn from(e: napi::Error) -> Self {
fn from(_e: napi::Error) -> Self {
ErrorStrategy
}
}
// Helper to convert NAPI parameters to JSON
fn napi_params_to_json(obj: napi::JsObject) -> Result<serde_json::Value> {
// For now, just extract the common parameters
let fast_period = obj.get_named_property::<f64>("fastPeriod").unwrap_or(5.0);
let slow_period = obj.get_named_property::<f64>("slowPeriod").unwrap_or(15.0);
Ok(serde_json::json!({
"fastPeriod": fast_period,
"slowPeriod": slow_period
}))
}