"""Tax lot tracking for cost basis and wash sale detection. Feature: autonomous-trading-engine Pure computation module for FIFO tax lot closing and wash sale detection within the 30-day window. Used by the Trading Engine for tax-loss harvesting awareness. """ from __future__ import annotations from dataclasses import dataclass from datetime import date, timedelta @dataclass class TaxLot: """A single tax lot representing a purchase of shares.""" ticker: str quantity: int cost_basis_per_share: float acquisition_date: date status: str = "open" # open | closed | washed @dataclass class ClosedLot: """Result of closing a tax lot via FIFO.""" ticker: str quantity: int cost_basis_per_share: float exit_price: float realized_pnl: float acquisition_date: date closed_date: date class TaxLotTracker: """Pure computation for FIFO lot closing and wash sale detection.""" def close_lots_fifo( self, lots: list[TaxLot], quantity: int, exit_price: float, exit_date: date, ) -> list[ClosedLot]: """Close lots in FIFO order (earliest acquired first). Processes open lots sorted by acquisition_date ascending, closing shares until the requested quantity is fulfilled. Returns a list of ClosedLot records with realized P&L. Parameters ---------- lots: All tax lots for the ticker (open ones will be selected). quantity: Number of shares to close. exit_price: Price per share at exit. exit_date: Date the lots are being closed. Returns ------- list[ClosedLot] Closed lot records in FIFO order. """ open_lots = sorted( [lot for lot in lots if lot.status == "open"], key=lambda lot: lot.acquisition_date, ) closed: list[ClosedLot] = [] remaining = quantity for lot in open_lots: if remaining <= 0: break close_qty = min(lot.quantity, remaining) realized_pnl = (exit_price - lot.cost_basis_per_share) * close_qty closed.append( ClosedLot( ticker=lot.ticker, quantity=close_qty, cost_basis_per_share=lot.cost_basis_per_share, exit_price=exit_price, realized_pnl=realized_pnl, acquisition_date=lot.acquisition_date, closed_date=exit_date, ) ) remaining -= close_qty return closed def check_wash_sale( self, loss_date: date, purchases: list[TaxLot], ) -> bool: """Check whether any purchase falls within the 30-day wash sale window. A wash sale occurs when the same ticker is purchased within 30 days before or after a loss-closing date. Parameters ---------- loss_date: The date the loss was realized. purchases: Tax lots representing purchases of the same ticker. Returns ------- bool True if any purchase is within the 30-day window. """ window_start = loss_date - timedelta(days=30) window_end = loss_date + timedelta(days=30) for lot in purchases: if window_start <= lot.acquisition_date <= window_end: return True return False