stock-bot/apps/stock/engine/src/backtest/strategy.rs
2025-07-04 11:24:27 -04:00

100 lines
No EOL
2.8 KiB
Rust

use chrono::{DateTime, Utc};
use serde::{Serialize, Deserialize};
use crate::MarketData;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SignalType {
Buy,
Sell,
Close,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Signal {
pub symbol: String,
pub signal_type: SignalType,
pub strength: f64, // -1.0 to 1.0
pub quantity: Option<f64>,
pub reason: Option<String>,
pub metadata: Option<serde_json::Value>,
}
// This trait will be implemented by Rust strategies
// TypeScript strategies will communicate through FFI
pub trait Strategy: Send + Sync {
fn on_market_data(&mut self, data: &MarketData) -> Vec<Signal>;
fn on_fill(&mut self, symbol: &str, quantity: f64, price: f64, side: &str);
fn get_name(&self) -> &str;
fn get_parameters(&self) -> serde_json::Value;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StrategyCall {
pub method: String,
pub data: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StrategyResponse {
pub signals: Vec<Signal>,
}
// Bridge for TypeScript strategies
// This will be used to wrap TypeScript strategies
pub struct TypeScriptStrategy {
pub name: String,
pub id: String,
pub parameters: serde_json::Value,
// Callback function will be injected from TypeScript
pub callback: Option<Box<dyn Fn(StrategyCall) -> StrategyResponse + Send + Sync>>,
}
impl TypeScriptStrategy {
pub fn new(name: String, id: String, parameters: serde_json::Value) -> Self {
Self {
name,
id,
parameters,
callback: None,
}
}
}
impl Strategy for TypeScriptStrategy {
fn on_market_data(&mut self, data: &MarketData) -> Vec<Signal> {
if let Some(callback) = &self.callback {
let call = StrategyCall {
method: "on_market_data".to_string(),
data: serde_json::to_value(data).unwrap_or_default(),
};
let response = callback(call);
response.signals
} else {
Vec::new()
}
}
fn on_fill(&mut self, symbol: &str, quantity: f64, price: f64, side: &str) {
if let Some(callback) = &self.callback {
let call = StrategyCall {
method: "on_fill".to_string(),
data: serde_json::json!({
"symbol": symbol,
"quantity": quantity,
"price": price,
"side": side
}),
};
callback(call);
}
}
fn get_name(&self) -> &str {
&self.name
}
fn get_parameters(&self) -> serde_json::Value {
self.parameters.clone()
}
}