use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct OpenPositionRequest {
pub pool_address: String,
pub tick_lower: i32,
pub tick_upper: i32,
pub amount_a: u64,
pub amount_b: u64,
#[serde(default = "default_slippage")]
pub slippage_tolerance_bps: u16,
}
fn default_slippage() -> u16 {
50
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct RebalanceRequest {
pub new_tick_lower: i32,
pub new_tick_upper: i32,
#[serde(default = "default_slippage")]
pub slippage_tolerance_bps: u16,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PositionResponse {
pub address: String,
pub pool_address: String,
pub owner: String,
pub tick_lower: i32,
pub tick_upper: i32,
pub liquidity: String,
pub in_range: bool,
#[schema(value_type = String)]
pub value_usd: Decimal,
pub pnl: PnLResponse,
pub status: PositionStatus,
#[schema(value_type = Option<String>)]
pub created_at: Option<chrono::DateTime<chrono::Utc>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PnLResponse {
#[schema(value_type = String)]
pub unrealized_pnl_usd: Decimal,
#[schema(value_type = String)]
pub unrealized_pnl_pct: Decimal,
pub fees_earned_a: u64,
pub fees_earned_b: u64,
#[schema(value_type = String)]
pub fees_earned_usd: Decimal,
#[schema(value_type = String)]
pub il_pct: Decimal,
#[schema(value_type = String)]
pub net_pnl_usd: Decimal,
#[schema(value_type = String)]
pub net_pnl_pct: Decimal,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum PositionStatus {
Active,
OutOfRange,
Closed,
Pending,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ListPositionsResponse {
pub positions: Vec<PositionResponse>,
pub total: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct CreateStrategyRequest {
pub name: String,
pub pool_address: String,
pub strategy_type: StrategyType,
pub parameters: StrategyParameters,
#[serde(default)]
pub auto_execute: bool,
#[serde(default)]
pub dry_run: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum StrategyType {
StaticRange,
Periodic,
Threshold,
IlLimit,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct StrategyParameters {
#[serde(skip_serializing_if = "Option::is_none")]
pub tick_width: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schema(value_type = Option<String>)]
pub rebalance_threshold_pct: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schema(value_type = Option<String>)]
pub max_il_pct: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
pub eval_interval_secs: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min_rebalance_interval_hours: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct StrategyResponse {
pub id: String,
pub name: String,
pub pool_address: String,
pub strategy_type: StrategyType,
pub parameters: StrategyParameters,
pub running: bool,
pub dry_run: bool,
#[schema(value_type = String)]
pub created_at: chrono::DateTime<chrono::Utc>,
#[schema(value_type = String)]
pub updated_at: chrono::DateTime<chrono::Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ListStrategiesResponse {
pub strategies: Vec<StrategyResponse>,
pub total: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct StrategyPerformanceResponse {
pub strategy_id: String,
#[schema(value_type = String)]
pub total_pnl_usd: Decimal,
#[schema(value_type = String)]
pub total_pnl_pct: Decimal,
#[schema(value_type = String)]
pub total_fees_usd: Decimal,
#[schema(value_type = String)]
pub total_il_pct: Decimal,
pub rebalance_count: u32,
pub total_tx_costs_lamports: u64,
#[schema(value_type = String)]
pub win_rate_pct: Decimal,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PoolResponse {
pub address: String,
pub protocol: String,
pub token_mint_a: String,
pub token_mint_b: String,
pub current_tick: i32,
pub tick_spacing: i32,
#[schema(value_type = String)]
pub price: Decimal,
pub liquidity: String,
pub fee_rate_bps: u16,
#[serde(skip_serializing_if = "Option::is_none")]
#[schema(value_type = Option<String>)]
pub volume_24h_usd: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schema(value_type = Option<String>)]
pub tvl_usd: Option<Decimal>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schema(value_type = Option<String>)]
pub apy_estimate: Option<Decimal>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ListPoolsResponse {
pub pools: Vec<PoolResponse>,
pub total: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PoolStateResponse {
pub address: String,
pub current_tick: i32,
pub sqrt_price: String,
#[schema(value_type = String)]
pub price: Decimal,
pub liquidity: String,
pub fee_growth_global_a: String,
pub fee_growth_global_b: String,
#[schema(value_type = String)]
pub timestamp: chrono::DateTime<chrono::Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PortfolioAnalyticsResponse {
#[schema(value_type = String)]
pub total_value_usd: Decimal,
#[schema(value_type = String)]
pub total_pnl_usd: Decimal,
#[schema(value_type = String)]
pub total_pnl_pct: Decimal,
#[schema(value_type = String)]
pub total_fees_usd: Decimal,
#[schema(value_type = String)]
pub total_il_pct: Decimal,
pub active_positions: u32,
pub positions_in_range: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub best_position: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub worst_position: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SimulationRequest {
pub pool_address: String,
pub tick_lower: i32,
pub tick_upper: i32,
#[schema(value_type = String)]
pub initial_capital_usd: Decimal,
#[schema(value_type = String)]
pub start_date: chrono::NaiveDate,
#[schema(value_type = String)]
pub end_date: chrono::NaiveDate,
#[serde(default)]
pub strategy_type: Option<StrategyType>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SimulationResponse {
pub id: String,
pub pool_address: String,
pub tick_lower: i32,
pub tick_upper: i32,
#[schema(value_type = String)]
pub initial_capital_usd: Decimal,
#[schema(value_type = String)]
pub final_value_usd: Decimal,
#[schema(value_type = String)]
pub total_return_pct: Decimal,
#[schema(value_type = String)]
pub fee_earnings_pct: Decimal,
#[schema(value_type = String)]
pub il_pct: Decimal,
#[schema(value_type = String)]
pub sharpe_ratio: Decimal,
#[schema(value_type = String)]
pub max_drawdown_pct: Decimal,
pub rebalance_count: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct HealthResponse {
pub status: ServiceStatus,
pub version: String,
pub uptime_secs: u64,
pub components: ComponentHealth,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum ServiceStatus {
Healthy,
Degraded,
Unhealthy,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ComponentHealth {
pub rpc: bool,
pub database: bool,
pub circuit_breaker: CircuitBreakerStatus,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum CircuitBreakerStatus {
Closed,
Open,
HalfOpen,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct MetricsResponse {
pub request_count: u64,
pub error_count: u64,
pub avg_response_time_ms: f64,
pub active_ws_connections: u32,
pub positions_monitored: u32,
pub strategies_running: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SuccessResponse<T> {
pub success: bool,
pub data: T,
}
impl<T> SuccessResponse<T> {
pub fn new(data: T) -> Self {
Self {
success: true,
data,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct MessageResponse {
pub message: String,
}
impl MessageResponse {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
}