use crate::error::Result;
use crate::types::{Bar, MarketTick, Order, OrderBook, OrderStatus, Position, Signal, Symbol};
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use rust_decimal::Decimal;
use tokio::sync::mpsc;
#[async_trait]
pub trait MarketDataProvider: Send + Sync {
async fn subscribe(&self, symbols: &[Symbol]) -> Result<mpsc::Receiver<MarketTick>>;
async fn unsubscribe(&self, symbols: &[Symbol]) -> Result<()>;
async fn get_latest_quote(&self, symbol: &Symbol) -> Result<MarketTick>;
async fn get_bars(
&self,
symbol: &Symbol,
start: DateTime<Utc>,
end: DateTime<Utc>,
timeframe: &str,
) -> Result<Vec<Bar>>;
async fn get_order_book(&self, symbol: &Symbol) -> Result<OrderBook>;
async fn is_connected(&self) -> bool;
}
#[async_trait]
pub trait Strategy: Send + Sync {
fn id(&self) -> &str;
fn name(&self) -> &str;
fn description(&self) -> &str;
async fn on_tick(&mut self, tick: &MarketTick) -> Result<Option<Signal>>;
async fn on_bar(&mut self, bar: &Bar) -> Result<Option<Signal>>;
async fn generate_signals(&mut self) -> Result<Vec<Signal>>;
async fn on_order_filled(
&mut self,
signal: &Signal,
order: &Order,
fill_price: Decimal,
) -> Result<()>;
async fn initialize(&mut self, bars: Vec<Bar>) -> Result<()>;
fn validate(&self) -> Result<()>;
fn symbols(&self) -> Vec<Symbol>;
fn risk_parameters(&self) -> StrategyRiskParameters;
}
#[derive(Debug, Clone)]
pub struct StrategyRiskParameters {
pub max_position_size: f64,
pub max_leverage: f64,
pub stop_loss_pct: f64,
pub take_profit_pct: f64,
}
impl Default for StrategyRiskParameters {
fn default() -> Self {
Self {
max_position_size: 0.1, max_leverage: 1.0, stop_loss_pct: 0.02, take_profit_pct: 0.05, }
}
}
#[async_trait]
pub trait ExecutionEngine: Send + Sync {
async fn place_order(&self, order: Order) -> Result<String>;
async fn cancel_order(&self, order_id: &str) -> Result<()>;
async fn get_order_status(&self, order_id: &str) -> Result<OrderStatus>;
async fn get_open_orders(&self) -> Result<Vec<Order>>;
async fn get_positions(&self) -> Result<Vec<Position>>;
async fn get_position(&self, symbol: &Symbol) -> Result<Option<Position>>;
async fn close_position(&self, symbol: &Symbol, percentage: f64) -> Result<()>;
async fn close_all_positions(&self) -> Result<()>;
async fn get_cash_balance(&self) -> Result<Decimal>;
async fn get_equity(&self) -> Result<Decimal>;
}
#[async_trait]
pub trait RiskManager: Send + Sync {
async fn validate_signal(
&self,
signal: &Signal,
portfolio_value: Decimal,
positions: &[Position],
) -> Result<()>;
async fn calculate_position_size(
&self,
signal: &Signal,
portfolio_value: Decimal,
risk_params: &StrategyRiskParameters,
) -> Result<Decimal>;
async fn check_daily_loss_limit(&self, current_pnl: Decimal) -> Result<()>;
async fn check_max_drawdown(&self, peak_equity: Decimal, current_equity: Decimal)
-> Result<()>;
async fn calculate_risk_metrics(&self, positions: &[Position]) -> Result<RiskMetrics>;
}
#[derive(Debug, Clone)]
pub struct RiskMetrics {
pub var_95: Decimal,
pub var_99: Decimal,
pub cvar_95: Decimal,
pub max_drawdown: Decimal,
pub sharpe_ratio: f64,
pub volatility: f64,
pub beta: f64,
}
#[async_trait]
pub trait PortfolioManager: Send + Sync {
async fn get_portfolio_value(&self) -> Result<Decimal>;
async fn get_positions(&self) -> Result<Vec<Position>>;
async fn update_position(
&self,
symbol: &Symbol,
quantity: Decimal,
price: Decimal,
) -> Result<()>;
async fn get_unrealized_pnl(&self) -> Result<Decimal>;
async fn get_realized_pnl(&self) -> Result<Decimal>;
async fn rebalance(&self, target_allocations: Vec<(Symbol, f64)>) -> Result<()>;
}
#[async_trait]
pub trait FeatureExtractor: Send + Sync {
async fn extract_features(&self, bars: &[Bar]) -> Result<Vec<FeatureVector>>;
fn feature_names(&self) -> Vec<String>;
}
#[derive(Debug, Clone)]
pub struct FeatureVector {
pub timestamp: DateTime<Utc>,
pub values: Vec<f64>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_strategy_risk_parameters_default() {
let params = StrategyRiskParameters::default();
assert_eq!(params.max_position_size, 0.1);
assert_eq!(params.max_leverage, 1.0);
assert_eq!(params.stop_loss_pct, 0.02);
assert_eq!(params.take_profit_pct, 0.05);
}
}