214 lines
5.2 KiB
Python
214 lines
5.2 KiB
Python
|
|
"""Pydantic schemas for request/response validation."""
|
||
|
|
|
||
|
|
from datetime import datetime, timezone
|
||
|
|
from decimal import Decimal
|
||
|
|
from typing import Optional, List, Dict, Any
|
||
|
|
from pydantic import BaseModel, Field, ConfigDict, field_validator
|
||
|
|
from enum import Enum
|
||
|
|
|
||
|
|
|
||
|
|
class OrderSide(str, Enum):
|
||
|
|
"""Order side."""
|
||
|
|
BUY = "buy"
|
||
|
|
SELL = "sell"
|
||
|
|
|
||
|
|
|
||
|
|
class OrderType(str, Enum):
|
||
|
|
"""Order type."""
|
||
|
|
MARKET = "market"
|
||
|
|
LIMIT = "limit"
|
||
|
|
STOP_LOSS = "stop_loss"
|
||
|
|
TAKE_PROFIT = "take_profit"
|
||
|
|
TRAILING_STOP = "trailing_stop"
|
||
|
|
OCO = "oco"
|
||
|
|
ICEBERG = "iceberg"
|
||
|
|
|
||
|
|
|
||
|
|
class OrderStatus(str, Enum):
|
||
|
|
"""Order status."""
|
||
|
|
PENDING = "pending"
|
||
|
|
OPEN = "open"
|
||
|
|
PARTIALLY_FILLED = "partially_filled"
|
||
|
|
FILLED = "filled"
|
||
|
|
CANCELLED = "cancelled"
|
||
|
|
REJECTED = "rejected"
|
||
|
|
EXPIRED = "expired"
|
||
|
|
|
||
|
|
|
||
|
|
# Trading Schemas
|
||
|
|
class OrderCreate(BaseModel):
|
||
|
|
"""Create order request."""
|
||
|
|
exchange_id: int
|
||
|
|
symbol: str
|
||
|
|
side: OrderSide
|
||
|
|
order_type: OrderType
|
||
|
|
quantity: Decimal
|
||
|
|
price: Optional[Decimal] = None
|
||
|
|
strategy_id: Optional[int] = None
|
||
|
|
paper_trading: bool = True
|
||
|
|
|
||
|
|
|
||
|
|
class OrderResponse(BaseModel):
|
||
|
|
"""Order response."""
|
||
|
|
model_config = ConfigDict(from_attributes=True, populate_by_name=True)
|
||
|
|
|
||
|
|
id: int
|
||
|
|
exchange_id: int
|
||
|
|
strategy_id: Optional[int]
|
||
|
|
symbol: str
|
||
|
|
order_type: OrderType
|
||
|
|
side: OrderSide
|
||
|
|
status: OrderStatus
|
||
|
|
quantity: Decimal
|
||
|
|
price: Optional[Decimal]
|
||
|
|
filled_quantity: Decimal
|
||
|
|
average_fill_price: Optional[Decimal]
|
||
|
|
fee: Decimal
|
||
|
|
paper_trading: bool
|
||
|
|
created_at: datetime
|
||
|
|
updated_at: datetime
|
||
|
|
filled_at: Optional[datetime]
|
||
|
|
|
||
|
|
@field_validator('created_at', 'updated_at', 'filled_at', mode='after')
|
||
|
|
@classmethod
|
||
|
|
def ensure_utc(cls, v: Optional[datetime]) -> Optional[datetime]:
|
||
|
|
if v and v.tzinfo is None:
|
||
|
|
return v.replace(tzinfo=timezone.utc)
|
||
|
|
return v
|
||
|
|
|
||
|
|
|
||
|
|
class PositionResponse(BaseModel):
|
||
|
|
"""Position response."""
|
||
|
|
model_config = ConfigDict(from_attributes=True, populate_by_name=True)
|
||
|
|
|
||
|
|
symbol: str
|
||
|
|
quantity: Decimal
|
||
|
|
entry_price: Decimal
|
||
|
|
current_price: Decimal
|
||
|
|
unrealized_pnl: Decimal
|
||
|
|
realized_pnl: Decimal
|
||
|
|
|
||
|
|
|
||
|
|
# Portfolio Schemas
|
||
|
|
class PortfolioResponse(BaseModel):
|
||
|
|
"""Portfolio response."""
|
||
|
|
positions: List[Dict[str, Any]]
|
||
|
|
performance: Dict[str, float]
|
||
|
|
timestamp: str
|
||
|
|
|
||
|
|
|
||
|
|
class PortfolioHistoryResponse(BaseModel):
|
||
|
|
"""Portfolio history response."""
|
||
|
|
dates: List[str]
|
||
|
|
values: List[float]
|
||
|
|
pnl: List[float]
|
||
|
|
|
||
|
|
|
||
|
|
# Strategy Schemas
|
||
|
|
class StrategyCreate(BaseModel):
|
||
|
|
"""Create strategy request."""
|
||
|
|
name: str
|
||
|
|
description: Optional[str] = None
|
||
|
|
strategy_type: str
|
||
|
|
class_name: str
|
||
|
|
parameters: Dict[str, Any] = Field(default_factory=dict)
|
||
|
|
timeframes: List[str] = Field(default_factory=lambda: ["1h"])
|
||
|
|
paper_trading: bool = True
|
||
|
|
schedule: Optional[Dict[str, Any]] = None
|
||
|
|
|
||
|
|
|
||
|
|
class StrategyUpdate(BaseModel):
|
||
|
|
"""Update strategy request."""
|
||
|
|
name: Optional[str] = None
|
||
|
|
description: Optional[str] = None
|
||
|
|
parameters: Optional[Dict[str, Any]] = None
|
||
|
|
timeframes: Optional[List[str]] = None
|
||
|
|
enabled: Optional[bool] = None
|
||
|
|
schedule: Optional[Dict[str, Any]] = None
|
||
|
|
|
||
|
|
|
||
|
|
class StrategyResponse(BaseModel):
|
||
|
|
"""Strategy response."""
|
||
|
|
model_config = ConfigDict(from_attributes=True, populate_by_name=True)
|
||
|
|
|
||
|
|
id: int
|
||
|
|
name: str
|
||
|
|
description: Optional[str]
|
||
|
|
strategy_type: str
|
||
|
|
class_name: str
|
||
|
|
parameters: Dict[str, Any]
|
||
|
|
timeframes: List[str]
|
||
|
|
enabled: bool
|
||
|
|
running: bool = False
|
||
|
|
paper_trading: bool
|
||
|
|
version: str
|
||
|
|
schedule: Optional[Dict[str, Any]]
|
||
|
|
created_at: datetime
|
||
|
|
updated_at: datetime
|
||
|
|
|
||
|
|
@field_validator('created_at', 'updated_at', mode='after')
|
||
|
|
@classmethod
|
||
|
|
def ensure_utc(cls, v: Optional[datetime]) -> Optional[datetime]:
|
||
|
|
if v and v.tzinfo is None:
|
||
|
|
return v.replace(tzinfo=timezone.utc)
|
||
|
|
return v
|
||
|
|
|
||
|
|
|
||
|
|
# Backtesting Schemas
|
||
|
|
class BacktestRequest(BaseModel):
|
||
|
|
"""Backtest request."""
|
||
|
|
strategy_id: int
|
||
|
|
symbol: str
|
||
|
|
exchange: str
|
||
|
|
timeframe: str
|
||
|
|
start_date: datetime
|
||
|
|
end_date: datetime
|
||
|
|
initial_capital: Decimal = Decimal("100.0")
|
||
|
|
slippage: float = 0.001
|
||
|
|
fee_rate: float = 0.001
|
||
|
|
|
||
|
|
|
||
|
|
class BacktestResponse(BaseModel):
|
||
|
|
"""Backtest response."""
|
||
|
|
backtest_id: Optional[int] = None
|
||
|
|
results: Dict[str, Any]
|
||
|
|
status: str = "completed"
|
||
|
|
|
||
|
|
|
||
|
|
# Exchange Schemas
|
||
|
|
class ExchangeResponse(BaseModel):
|
||
|
|
"""Exchange response."""
|
||
|
|
model_config = ConfigDict(from_attributes=True, populate_by_name=True)
|
||
|
|
|
||
|
|
id: int
|
||
|
|
name: str
|
||
|
|
sandbox: bool
|
||
|
|
read_only: bool
|
||
|
|
enabled: bool
|
||
|
|
created_at: datetime
|
||
|
|
updated_at: datetime
|
||
|
|
|
||
|
|
@field_validator('created_at', 'updated_at', mode='after')
|
||
|
|
@classmethod
|
||
|
|
def ensure_utc(cls, v: Optional[datetime]) -> Optional[datetime]:
|
||
|
|
if v and v.tzinfo is None:
|
||
|
|
return v.replace(tzinfo=timezone.utc)
|
||
|
|
return v
|
||
|
|
|
||
|
|
|
||
|
|
# WebSocket Messages
|
||
|
|
class PriceUpdate(BaseModel):
|
||
|
|
"""Price update message."""
|
||
|
|
exchange: str
|
||
|
|
symbol: str
|
||
|
|
price: Decimal
|
||
|
|
timestamp: datetime
|
||
|
|
|
||
|
|
|
||
|
|
class OrderUpdate(BaseModel):
|
||
|
|
"""Order update message."""
|
||
|
|
order_id: int
|
||
|
|
status: OrderStatus
|
||
|
|
filled_quantity: Optional[Decimal] = None
|
||
|
|
timestamp: datetime
|