Phase 1: - Real-time signal alerts (Discord/Telegram webhook) - Trading journal with entry/exit tracking - Position sizing calculator (Fixed/Kelly/ATR) Phase 2: - Pension asset allocation (DC/IRP 70% risk limit) - Drawdown monitoring with SVG gauge - Benchmark dashboard (portfolio vs KOSPI vs deposit) Phase 3: - Tax benefit simulation (Korean pension tax rules) - Correlation matrix heatmap - Parameter optimizer with grid search + overfit detection
94 lines
2.8 KiB
Python
94 lines
2.8 KiB
Python
"""
|
|
Trading journal Pydantic schemas.
|
|
"""
|
|
from datetime import date, datetime
|
|
from decimal import Decimal
|
|
from typing import Optional
|
|
from enum import Enum
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
from app.schemas.portfolio import FloatDecimal
|
|
|
|
|
|
class TradeType(str, Enum):
|
|
BUY = "buy"
|
|
SELL = "sell"
|
|
|
|
|
|
class JournalStatus(str, Enum):
|
|
OPEN = "open"
|
|
CLOSED = "closed"
|
|
|
|
|
|
class TradeJournalCreate(BaseModel):
|
|
stock_code: str = Field(..., min_length=1, max_length=20)
|
|
stock_name: Optional[str] = Field(None, max_length=100)
|
|
trade_type: TradeType
|
|
entry_price: Optional[FloatDecimal] = Field(None, gt=0)
|
|
target_price: Optional[FloatDecimal] = Field(None, gt=0)
|
|
stop_loss_price: Optional[FloatDecimal] = Field(None, gt=0)
|
|
entry_date: date
|
|
quantity: Optional[int] = Field(None, gt=0)
|
|
entry_reason: Optional[str] = None
|
|
scenario: Optional[str] = None
|
|
emotional_state: Optional[str] = None
|
|
strategy_id: Optional[int] = None
|
|
|
|
|
|
class TradeJournalUpdate(BaseModel):
|
|
stock_name: Optional[str] = Field(None, max_length=100)
|
|
exit_price: Optional[FloatDecimal] = Field(None, gt=0)
|
|
exit_date: Optional[date] = None
|
|
exit_reason: Optional[str] = None
|
|
target_price: Optional[FloatDecimal] = Field(None, gt=0)
|
|
stop_loss_price: Optional[FloatDecimal] = Field(None, gt=0)
|
|
quantity: Optional[int] = Field(None, gt=0)
|
|
lessons_learned: Optional[str] = None
|
|
emotional_state: Optional[str] = None
|
|
scenario: Optional[str] = None
|
|
entry_reason: Optional[str] = None
|
|
status: Optional[JournalStatus] = None
|
|
|
|
|
|
class TradeJournalResponse(BaseModel):
|
|
id: int
|
|
user_id: int
|
|
stock_code: str
|
|
stock_name: Optional[str] = None
|
|
trade_type: str
|
|
entry_price: Optional[FloatDecimal] = None
|
|
target_price: Optional[FloatDecimal] = None
|
|
stop_loss_price: Optional[FloatDecimal] = None
|
|
exit_price: Optional[FloatDecimal] = None
|
|
entry_date: date
|
|
exit_date: Optional[date] = None
|
|
quantity: Optional[int] = None
|
|
profit_loss: Optional[FloatDecimal] = None
|
|
profit_loss_pct: Optional[FloatDecimal] = None
|
|
entry_reason: Optional[str] = None
|
|
exit_reason: Optional[str] = None
|
|
scenario: Optional[str] = None
|
|
lessons_learned: Optional[str] = None
|
|
emotional_state: Optional[str] = None
|
|
strategy_id: Optional[int] = None
|
|
status: str
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class TradeJournalStats(BaseModel):
|
|
total_trades: int = 0
|
|
open_trades: int = 0
|
|
closed_trades: int = 0
|
|
win_count: int = 0
|
|
loss_count: int = 0
|
|
win_rate: Optional[FloatDecimal] = None
|
|
avg_profit_loss_pct: Optional[FloatDecimal] = None
|
|
max_profit_pct: Optional[FloatDecimal] = None
|
|
max_loss_pct: Optional[FloatDecimal] = None
|
|
total_profit_loss: Optional[FloatDecimal] = None
|