added rust engine and adapter pattern
This commit is contained in:
parent
a58072cf93
commit
0a4702d12a
6 changed files with 328 additions and 186 deletions
Binary file not shown.
|
|
@ -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
|
||||
}))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue