Files
myTradeTracker/backend/app/models/position.py
Chris eea4469095 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>
2026-01-22 14:27:43 -05:00

105 lines
3.9 KiB
Python

"""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")