use std::collections::HashMap;
use std::path::Path;
use std::fs::{self, File};
use std::io::{Read, Write};
use chrono::{DateTime, FixedOffset};
use serde::{Serialize, Deserialize};
use tracing::{info, warn, error};
use crate::unified_data_impl::{
MarketData, OrderRequest, OrderResult, Signal, FundingPayment
};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum StrategyParam {
String(String),
Number(f64),
Boolean(bool),
StringArray(Vec<String>),
NumberArray(Vec<f64>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StrategyConfig {
pub name: String,
pub description: String,
pub version: String,
pub parameters: HashMap<String, StrategyParam>,
pub metadata: HashMap<String, String>,
}
impl StrategyConfig {
pub fn new(name: &str, description: &str, version: &str) -> Self {
Self {
name: name.to_string(),
description: description.to_string(),
version: version.to_string(),
parameters: HashMap::new(),
metadata: HashMap::new(),
}
}
pub fn with_string_param(mut self, key: &str, value: &str) -> Self {
self.parameters.insert(key.to_string(), StrategyParam::String(value.to_string()));
self
}
pub fn with_number_param(mut self, key: &str, value: f64) -> Self {
self.parameters.insert(key.to_string(), StrategyParam::Number(value));
self
}
pub fn with_bool_param(mut self, key: &str, value: bool) -> Self {
self.parameters.insert(key.to_string(), StrategyParam::Boolean(value));
self
}
pub fn with_string_array_param(mut self, key: &str, values: Vec<String>) -> Self {
self.parameters.insert(key.to_string(), StrategyParam::StringArray(values));
self
}
pub fn with_number_array_param(mut self, key: &str, values: Vec<f64>) -> Self {
self.parameters.insert(key.to_string(), StrategyParam::NumberArray(values));
self
}
pub fn with_metadata(mut self, key: &str, value: &str) -> Self {
self.metadata.insert(key.to_string(), value.to_string());
self
}
pub fn get_string(&self, key: &str) -> Option<&String> {
match self.parameters.get(key) {
Some(StrategyParam::String(value)) => Some(value),
_ => None,
}
}
pub fn get_number(&self, key: &str) -> Option<f64> {
match self.parameters.get(key) {
Some(StrategyParam::Number(value)) => Some(*value),
_ => None,
}
}
pub fn get_bool(&self, key: &str) -> Option<bool> {
match self.parameters.get(key) {
Some(StrategyParam::Boolean(value)) => Some(*value),
_ => None,
}
}
pub fn get_string_array(&self, key: &str) -> Option<&Vec<String>> {
match self.parameters.get(key) {
Some(StrategyParam::StringArray(value)) => Some(value),
_ => None,
}
}
pub fn get_number_array(&self, key: &str) -> Option<&Vec<f64>> {
match self.parameters.get(key) {
Some(StrategyParam::NumberArray(value)) => Some(value),
_ => None,
}
}
pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<(), String> {
let json = serde_json::to_string_pretty(self)
.map_err(|e| format!("Failed to serialize strategy config: {}", e))?;
fs::write(path, json)
.map_err(|e| format!("Failed to write strategy config: {}", e))?;
Ok(())
}
pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self, String> {
let mut file = File::open(path)
.map_err(|e| format!("Failed to open strategy config file: {}", e))?;
let mut contents = String::new();
file.read_to_string(&mut contents)
.map_err(|e| format!("Failed to read strategy config file: {}", e))?;
serde_json::from_str(&contents)
.map_err(|e| format!("Failed to parse strategy config: {}", e))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StrategyState {
pub name: String,
pub version: String,
pub last_updated: DateTime<FixedOffset>,
pub positions: HashMap<String, f64>,
pub signals: HashMap<String, String>,
pub metrics: HashMap<String, f64>,
pub custom_data: HashMap<String, serde_json::Value>,
}
impl StrategyState {
pub fn new(name: &str, version: &str, timestamp: DateTime<FixedOffset>) -> Self {
Self {
name: name.to_string(),
version: version.to_string(),
last_updated: timestamp,
positions: HashMap::new(),
signals: HashMap::new(),
metrics: HashMap::new(),
custom_data: HashMap::new(),
}
}
pub fn update_timestamp(&mut self, timestamp: DateTime<FixedOffset>) {
self.last_updated = timestamp;
}
pub fn set_position(&mut self, symbol: &str, size: f64) {
self.positions.insert(symbol.to_string(), size);
}
pub fn remove_position(&mut self, symbol: &str) {
self.positions.remove(symbol);
}
pub fn set_signal(&mut self, symbol: &str, signal: &str) {
self.signals.insert(symbol.to_string(), signal.to_string());
}
pub fn remove_signal(&mut self, symbol: &str) {
self.signals.remove(symbol);
}
pub fn set_metric(&mut self, key: &str, value: f64) {
self.metrics.insert(key.to_string(), value);
}
pub fn set_custom_data<T: Serialize>(&mut self, key: &str, value: &T) -> Result<(), String> {
let json_value = serde_json::to_value(value)
.map_err(|e| format!("Failed to serialize custom data: {}", e))?;
self.custom_data.insert(key.to_string(), json_value);
Ok(())
}
pub fn get_custom_data<T: for<'de> Deserialize<'de>>(&self, key: &str) -> Result<Option<T>, String> {
match self.custom_data.get(key) {
Some(value) => {
let deserialized = serde_json::from_value(value.clone())
.map_err(|e| format!("Failed to deserialize custom data: {}", e))?;
Ok(Some(deserialized))
},
None => Ok(None),
}
}
pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<(), String> {
let json = serde_json::to_string_pretty(self)
.map_err(|e| format!("Failed to serialize strategy state: {}", e))?;
fs::write(path, json)
.map_err(|e| format!("Failed to write strategy state: {}", e))?;
Ok(())
}
pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self, String> {
let mut file = File::open(path)
.map_err(|e| format!("Failed to open strategy state file: {}", e))?;
let mut contents = String::new();
file.read_to_string(&mut contents)
.map_err(|e| format!("Failed to read strategy state file: {}", e))?;
serde_json::from_str(&contents)
.map_err(|e| format!("Failed to parse strategy state: {}", e))
}
}
pub trait TradingStrategy: Send + Sync {
fn name(&self) -> &str;
fn config(&self) -> &StrategyConfig;
fn config_mut(&mut self) -> &mut StrategyConfig;
fn update_config(&mut self, config: StrategyConfig) -> Result<(), String>;
fn state(&self) -> &StrategyState;
fn state_mut(&mut self) -> &mut StrategyState;
fn on_market_data(&mut self, data: &MarketData) -> Result<Vec<OrderRequest>, String>;
fn on_order_fill(&mut self, fill: &OrderResult) -> Result<(), String>;
fn on_funding_payment(&mut self, payment: &FundingPayment) -> Result<(), String>;
fn get_current_signals(&self) -> HashMap<String, Signal>;
fn initialize(&mut self) -> Result<(), String> {
Ok(())
}
fn shutdown(&mut self) -> Result<(), String> {
Ok(())
}
fn save_state<P: AsRef<Path>>(&self, path: P) -> Result<(), String> {
self.state().save_to_file(path)
}
fn load_state<P: AsRef<Path>>(&mut self, path: P) -> Result<(), String> {
let state = StrategyState::load_from_file(path)?;
if state.name != self.name() {
return Err(format!(
"Strategy name mismatch: expected {}, found {}",
self.name(),
state.name
));
}
*self.state_mut() = state;
Ok(())
}
fn save_config<P: AsRef<Path>>(&self, path: P) -> Result<(), String> {
self.config().save_to_file(path)
}
fn load_config<P: AsRef<Path>>(&mut self, path: P) -> Result<(), String> {
let config = StrategyConfig::load_from_file(path)?;
if config.name != self.name() {
return Err(format!(
"Strategy name mismatch: expected {}, found {}",
self.name(),
config.name
));
}
self.update_config(config)
}
fn on_event(&mut self, event_type: &str, data: &serde_json::Value) -> Result<(), String> {
Ok(())
}
}
pub struct BaseTradingStrategy {
config: StrategyConfig,
state: StrategyState,
}
impl BaseTradingStrategy {
pub fn new(name: &str, description: &str, version: &str) -> Self {
let config = StrategyConfig::new(name, description, version);
let state = StrategyState::new(
name,
version,
chrono::Utc::now().with_timezone(&chrono::FixedOffset::east(0)),
);
Self { config, state }
}
}
impl TradingStrategy for BaseTradingStrategy {
fn name(&self) -> &str {
&self.config.name
}
fn config(&self) -> &StrategyConfig {
&self.config
}
fn config_mut(&mut self) -> &mut StrategyConfig {
&mut self.config
}
fn update_config(&mut self, config: StrategyConfig) -> Result<(), String> {
if config.name != self.config.name {
return Err(format!(
"Strategy name mismatch: expected {}, found {}",
self.config.name,
config.name
));
}
self.config = config;
Ok(())
}
fn state(&self) -> &StrategyState {
&self.state
}
fn state_mut(&mut self) -> &mut StrategyState {
&mut self.state
}
fn on_market_data(&mut self, _data: &MarketData) -> Result<Vec<OrderRequest>, String> {
Ok(Vec::new())
}
fn on_order_fill(&mut self, _fill: &OrderResult) -> Result<(), String> {
Ok(())
}
fn on_funding_payment(&mut self, _payment: &FundingPayment) -> Result<(), String> {
Ok(())
}
fn get_current_signals(&self) -> HashMap<String, Signal> {
HashMap::new()
}
}