use std::collections::HashMap;
use chrono::{DateTime, FixedOffset, Utc};
use tracing::{info, debug, warn};
use crate::strategies::trading_strategy::{
TradingStrategy, StrategyConfig, StrategyState, BaseTradingStrategy
};
use crate::unified_data_impl::{
MarketData, OrderRequest, OrderResult, Signal, FundingPayment,
SignalDirection, OrderSide, OrderType
};
pub struct StrategyTemplate {
base: BaseTradingStrategy,
signals: HashMap<String, Signal>,
parameter1: f64,
parameter2: bool,
}
impl StrategyTemplate {
pub fn new(parameter1: f64, parameter2: bool) -> Self {
let config = StrategyConfig::new(
"StrategyTemplate",
"Template for creating new strategies",
"1.0.0",
)
.with_number_param("parameter1", parameter1)
.with_bool_param("parameter2", parameter2);
let mut base = BaseTradingStrategy::new(
"StrategyTemplate",
"Template for creating new strategies",
"1.0.0",
);
base.update_config(config).expect("Failed to update config");
Self {
base,
signals: HashMap::new(),
parameter1,
parameter2,
}
}
fn generate_signal(&self, data: &MarketData) -> Signal {
let direction = if data.price > data.mid_price() {
SignalDirection::Buy
} else if data.price < data.mid_price() {
SignalDirection::Sell
} else {
SignalDirection::Neutral
};
let strength = if self.parameter2 {
0.8
} else {
0.5
};
let mut metadata = HashMap::new();
metadata.insert("strategy_type".to_string(), "template".to_string());
metadata.insert("parameter1".to_string(), self.parameter1.to_string());
metadata.insert("parameter2".to_string(), self.parameter2.to_string());
Signal {
symbol: data.symbol.clone(),
direction,
strength,
timestamp: data.timestamp,
metadata,
}
}
}
impl TradingStrategy for StrategyTemplate {
fn name(&self) -> &str {
self.base.name()
}
fn config(&self) -> &StrategyConfig {
self.base.config()
}
fn config_mut(&mut self) -> &mut StrategyConfig {
self.base.config_mut()
}
fn update_config(&mut self, config: StrategyConfig) -> Result<(), String> {
if let Some(parameter1) = config.get_number("parameter1") {
self.parameter1 = parameter1;
}
if let Some(parameter2) = config.get_bool("parameter2") {
self.parameter2 = parameter2;
}
self.base.update_config(config)
}
fn state(&self) -> &StrategyState {
self.base.state()
}
fn state_mut(&mut self) -> &mut StrategyState {
self.base.state_mut()
}
fn on_market_data(&mut self, data: &MarketData) -> Result<Vec<OrderRequest>, String> {
let signal = self.generate_signal(data);
self.signals.insert(data.symbol.clone(), signal.clone());
self.state_mut().set_signal(&data.symbol, match signal.direction {
SignalDirection::Buy => "long",
SignalDirection::Sell => "short",
_ => "neutral",
});
let orders = match signal.direction {
SignalDirection::Buy => {
let position_size = signal.strength * self.parameter1;
vec![OrderRequest::market(&data.symbol, OrderSide::Buy, position_size)]
},
SignalDirection::Sell => {
let position_size = signal.strength * self.parameter1;
vec![OrderRequest::market(&data.symbol, OrderSide::Sell, position_size)]
},
_ => {
vec![]
}
};
self.state_mut().update_timestamp(data.timestamp);
Ok(orders)
}
fn on_order_fill(&mut self, fill: &OrderResult) -> Result<(), String> {
let position_size = match fill.side {
OrderSide::Buy => fill.filled_quantity,
OrderSide::Sell => -fill.filled_quantity,
};
let current_position = self.state().positions.get(&fill.symbol).copied().unwrap_or(0.0);
self.state_mut().set_position(&fill.symbol, current_position + position_size);
if let Some(price) = fill.average_price {
self.state_mut().set_metric(&format!("{}_last_fill_price", fill.symbol), price);
}
if let Some(fees) = fill.fees {
let current_fees = self.state().metrics.get("total_fees").copied().unwrap_or(0.0);
self.state_mut().set_metric("total_fees", current_fees + fees);
}
info!(
"Order filled: {} {} {} at {} with size {}",
fill.symbol,
fill.side,
fill.order_type,
fill.average_price.unwrap_or(0.0),
fill.filled_quantity
);
Ok(())
}
fn on_funding_payment(&mut self, payment: &FundingPayment) -> Result<(), String> {
let key = format!("{}_funding_payment", payment.symbol);
let current_payment = self.state().metrics.get(&key).copied().unwrap_or(0.0);
self.state_mut().set_metric(&key, current_payment + payment.amount);
let total_key = if payment.amount > 0.0 {
"total_funding_received"
} else {
"total_funding_paid"
};
let current_total = self.state().metrics.get(total_key).copied().unwrap_or(0.0);
self.state_mut().set_metric(total_key, current_total + payment.amount.abs());
info!(
"Funding payment for {}: rate={}, amount={}, position={}",
payment.symbol,
payment.rate,
payment.amount,
payment.position_size
);
Ok(())
}
fn get_current_signals(&self) -> HashMap<String, Signal> {
self.signals.clone()
}
fn initialize(&mut self) -> Result<(), String> {
info!(
"Initializing StrategyTemplate with parameter1={}, parameter2={}",
self.parameter1,
self.parameter2
);
Ok(())
}
fn shutdown(&mut self) -> Result<(), String> {
info!("Shutting down StrategyTemplate");
Ok(())
}
}
pub fn create_strategy_template(parameter1: f64, parameter2: bool) -> Result<Box<dyn TradingStrategy>, String> {
if parameter1 <= 0.0 {
return Err("parameter1 must be positive".to_string());
}
Ok(Box::new(StrategyTemplate::new(parameter1, parameter2)))
}