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:
99
backend/app/parsers/base_parser.py
Normal file
99
backend/app/parsers/base_parser.py
Normal file
@@ -0,0 +1,99 @@
|
||||
"""Base parser interface for brokerage CSV files."""
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List, Dict, Any, NamedTuple
|
||||
from pathlib import Path
|
||||
import pandas as pd
|
||||
|
||||
|
||||
class ParseResult(NamedTuple):
|
||||
"""
|
||||
Result of parsing a brokerage CSV file.
|
||||
|
||||
Attributes:
|
||||
transactions: List of parsed transaction dictionaries
|
||||
errors: List of error messages encountered during parsing
|
||||
row_count: Total number of rows processed
|
||||
"""
|
||||
transactions: List[Dict[str, Any]]
|
||||
errors: List[str]
|
||||
row_count: int
|
||||
|
||||
|
||||
class BaseParser(ABC):
|
||||
"""
|
||||
Abstract base class for brokerage CSV parsers.
|
||||
|
||||
Provides a standard interface for parsing CSV files from different brokerages.
|
||||
Subclasses must implement the parse() method for their specific format.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def parse(self, file_path: Path) -> ParseResult:
|
||||
"""
|
||||
Parse a brokerage CSV file into standardized transaction dictionaries.
|
||||
|
||||
Args:
|
||||
file_path: Path to the CSV file to parse
|
||||
|
||||
Returns:
|
||||
ParseResult containing transactions, errors, and row count
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If the file does not exist
|
||||
ValueError: If the file format is invalid
|
||||
"""
|
||||
pass
|
||||
|
||||
def _read_csv(self, file_path: Path, **kwargs) -> pd.DataFrame:
|
||||
"""
|
||||
Read CSV file into a pandas DataFrame with error handling.
|
||||
|
||||
Args:
|
||||
file_path: Path to CSV file
|
||||
**kwargs: Additional arguments passed to pd.read_csv()
|
||||
|
||||
Returns:
|
||||
DataFrame containing CSV data
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If file does not exist
|
||||
pd.errors.EmptyDataError: If file is empty
|
||||
"""
|
||||
if not file_path.exists():
|
||||
raise FileNotFoundError(f"CSV file not found: {file_path}")
|
||||
|
||||
return pd.read_csv(file_path, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def _safe_decimal(value: Any) -> Any:
|
||||
"""
|
||||
Safely convert value to decimal-compatible format, handling NaN and None.
|
||||
|
||||
Args:
|
||||
value: Value to convert
|
||||
|
||||
Returns:
|
||||
Converted value or None if invalid
|
||||
"""
|
||||
if pd.isna(value):
|
||||
return None
|
||||
if value == "":
|
||||
return None
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def _safe_date(value: Any) -> Any:
|
||||
"""
|
||||
Safely convert value to date, handling NaN and None.
|
||||
|
||||
Args:
|
||||
value: Value to convert
|
||||
|
||||
Returns:
|
||||
Converted date or None if invalid
|
||||
"""
|
||||
if pd.isna(value):
|
||||
return None
|
||||
if value == "":
|
||||
return None
|
||||
return value
|
||||
Reference in New Issue
Block a user