Some checks are pending
Documentation / build-docs (push) Waiting to run
Tests / test (macos-latest, 3.11) (push) Waiting to run
Tests / test (macos-latest, 3.12) (push) Waiting to run
Tests / test (macos-latest, 3.13) (push) Waiting to run
Tests / test (macos-latest, 3.14) (push) Waiting to run
Tests / test (ubuntu-latest, 3.11) (push) Waiting to run
Tests / test (ubuntu-latest, 3.12) (push) Waiting to run
Tests / test (ubuntu-latest, 3.13) (push) Waiting to run
Tests / test (ubuntu-latest, 3.14) (push) Waiting to run
255 lines
6.5 KiB
Python
255 lines
6.5 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
|
|
# Advanced order parameters
|
|
take_profit_price: Optional[Decimal] = None
|
|
stop_loss_price: Optional[Decimal] = None
|
|
trail_percent: Optional[Decimal] = None # For trailing stop (e.g., 0.02 for 2%)
|
|
# For bracket orders
|
|
bracket_take_profit: Optional[Decimal] = None
|
|
bracket_stop_loss: Optional[Decimal] = None
|
|
# For OCO orders
|
|
oco_price: Optional[Decimal] = None # Second order price for OCO
|
|
# For iceberg orders
|
|
visible_quantity: Optional[Decimal] = None # Visible quantity for iceberg
|
|
|
|
|
|
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 WalkForwardRequest(BaseModel):
|
|
"""Walk-forward analysis request."""
|
|
strategy_id: int
|
|
symbol: str
|
|
exchange: str
|
|
timeframe: str
|
|
start_date: datetime
|
|
end_date: datetime
|
|
train_period_days: int = 90
|
|
test_period_days: int = 30
|
|
step_days: int = 30
|
|
initial_capital: Decimal = Decimal("10000.0")
|
|
parameter_grid: Optional[Dict[str, List[Any]]] = None
|
|
optimization_metric: str = "sharpe_ratio"
|
|
|
|
|
|
class MonteCarloRequest(BaseModel):
|
|
"""Monte Carlo simulation request."""
|
|
strategy_id: int
|
|
symbol: str
|
|
exchange: str
|
|
timeframe: str
|
|
start_date: datetime
|
|
end_date: datetime
|
|
initial_capital: Decimal = Decimal("10000.0")
|
|
num_simulations: int = 1000
|
|
parameter_ranges: Optional[Dict[str, List[float]]] = None # {param_name: [min, max]}
|
|
random_seed: Optional[int] = None
|
|
|
|
|
|
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
|