Initial release v1.1.0
- Complete MVP for tracking Fidelity brokerage account performance - Transaction import from CSV with deduplication - Automatic FIFO position tracking with options support - Real-time P&L calculations with market data caching - Dashboard with timeframe filtering (30/90/180 days, 1 year, YTD, all time) - Docker-based deployment with PostgreSQL backend - React/TypeScript frontend with TailwindCSS - FastAPI backend with SQLAlchemy ORM Features: - Multi-account support - Import via CSV upload or filesystem - Open and closed position tracking - Balance history charting - Performance analytics and metrics - Top trades analysis - Responsive UI design Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
104
backend/app/models/position.py
Normal file
104
backend/app/models/position.py
Normal file
@@ -0,0 +1,104 @@
|
||||
"""Position model representing a trading position."""
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Numeric, ForeignKey, Date, Enum, Index
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql import func
|
||||
import enum
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class PositionType(str, enum.Enum):
|
||||
"""Enumeration of position types."""
|
||||
STOCK = "stock"
|
||||
CALL = "call"
|
||||
PUT = "put"
|
||||
|
||||
|
||||
class PositionStatus(str, enum.Enum):
|
||||
"""Enumeration of position statuses."""
|
||||
OPEN = "open"
|
||||
CLOSED = "closed"
|
||||
|
||||
|
||||
class Position(Base):
|
||||
"""
|
||||
Represents a trading position (open or closed).
|
||||
|
||||
A position aggregates related transactions (entries and exits) for a specific security.
|
||||
For options, tracks strikes, expirations, and option-specific details.
|
||||
|
||||
Attributes:
|
||||
id: Primary key
|
||||
account_id: Foreign key to account
|
||||
symbol: Base trading symbol (e.g., AAPL)
|
||||
option_symbol: Full option symbol if applicable (e.g., -AAPL260116C150)
|
||||
position_type: Type (stock, call, put)
|
||||
status: Status (open, closed)
|
||||
open_date: Date position was opened
|
||||
close_date: Date position was closed (if closed)
|
||||
total_quantity: Net quantity (can be negative for short positions)
|
||||
avg_entry_price: Average entry price
|
||||
avg_exit_price: Average exit price (if closed)
|
||||
realized_pnl: Realized profit/loss for closed positions
|
||||
unrealized_pnl: Unrealized profit/loss for open positions
|
||||
created_at: Timestamp of record creation
|
||||
updated_at: Timestamp of last update
|
||||
"""
|
||||
__tablename__ = "positions"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
account_id = Column(Integer, ForeignKey("accounts.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
|
||||
# Symbol information
|
||||
symbol = Column(String(50), nullable=False, index=True)
|
||||
option_symbol = Column(String(100), index=True) # Full option symbol for options
|
||||
position_type = Column(Enum(PositionType), nullable=False, default=PositionType.STOCK)
|
||||
|
||||
# Status and dates
|
||||
status = Column(Enum(PositionStatus), nullable=False, default=PositionStatus.OPEN, index=True)
|
||||
open_date = Column(Date, nullable=False)
|
||||
close_date = Column(Date)
|
||||
|
||||
# Position metrics
|
||||
total_quantity = Column(Numeric(20, 8), nullable=False) # Can be negative for short
|
||||
avg_entry_price = Column(Numeric(20, 8))
|
||||
avg_exit_price = Column(Numeric(20, 8))
|
||||
|
||||
# P&L tracking
|
||||
realized_pnl = Column(Numeric(20, 2)) # For closed positions
|
||||
unrealized_pnl = Column(Numeric(20, 2)) # For open positions
|
||||
|
||||
# Timestamps
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now(), server_default=func.now(), nullable=False)
|
||||
|
||||
# Relationships
|
||||
account = relationship("Account", back_populates="positions")
|
||||
transaction_links = relationship("PositionTransaction", back_populates="position", cascade="all, delete-orphan")
|
||||
|
||||
# Composite indexes for common queries
|
||||
__table_args__ = (
|
||||
Index('idx_account_status', 'account_id', 'status'),
|
||||
Index('idx_account_symbol_status', 'account_id', 'symbol', 'status'),
|
||||
)
|
||||
|
||||
|
||||
class PositionTransaction(Base):
|
||||
"""
|
||||
Junction table linking positions to transactions.
|
||||
|
||||
A position can have multiple transactions (entries, exits, adjustments).
|
||||
A transaction can be part of multiple positions (e.g., closing multiple lots).
|
||||
|
||||
Attributes:
|
||||
position_id: Foreign key to position
|
||||
transaction_id: Foreign key to transaction
|
||||
"""
|
||||
__tablename__ = "position_transactions"
|
||||
|
||||
position_id = Column(Integer, ForeignKey("positions.id", ondelete="CASCADE"), primary_key=True)
|
||||
transaction_id = Column(Integer, ForeignKey("transactions.id", ondelete="CASCADE"), primary_key=True)
|
||||
|
||||
# Relationships
|
||||
position = relationship("Position", back_populates="transaction_links")
|
||||
transaction = relationship("Transaction", back_populates="position_links")
|
||||
Reference in New Issue
Block a user