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:
5
backend/app/utils/__init__.py
Normal file
5
backend/app/utils/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
"""Utility functions and helpers."""
|
||||
from app.utils.deduplication import generate_transaction_hash
|
||||
from app.utils.option_parser import parse_option_symbol, OptionInfo
|
||||
|
||||
__all__ = ["generate_transaction_hash", "parse_option_symbol", "OptionInfo"]
|
||||
65
backend/app/utils/deduplication.py
Normal file
65
backend/app/utils/deduplication.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""Transaction deduplication utilities."""
|
||||
import hashlib
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def generate_transaction_hash(
|
||||
account_id: int,
|
||||
run_date: date,
|
||||
symbol: Optional[str],
|
||||
action: str,
|
||||
amount: Optional[Decimal],
|
||||
quantity: Optional[Decimal],
|
||||
price: Optional[Decimal],
|
||||
) -> str:
|
||||
"""
|
||||
Generate a unique SHA-256 hash for a transaction to prevent duplicates.
|
||||
|
||||
The hash is generated from key transaction attributes that uniquely identify
|
||||
a transaction: account, date, symbol, action, amount, quantity, and price.
|
||||
|
||||
Args:
|
||||
account_id: Account identifier
|
||||
run_date: Transaction date
|
||||
symbol: Trading symbol
|
||||
action: Transaction action description
|
||||
amount: Transaction amount
|
||||
quantity: Number of shares/contracts
|
||||
price: Price per unit
|
||||
|
||||
Returns:
|
||||
str: 64-character hexadecimal SHA-256 hash
|
||||
|
||||
Example:
|
||||
>>> generate_transaction_hash(
|
||||
... account_id=1,
|
||||
... run_date=date(2025, 12, 26),
|
||||
... symbol="AAPL",
|
||||
... action="YOU BOUGHT",
|
||||
... amount=Decimal("-1500.00"),
|
||||
... quantity=Decimal("10"),
|
||||
... price=Decimal("150.00")
|
||||
... )
|
||||
'a1b2c3d4...'
|
||||
"""
|
||||
# Convert values to strings, handling None values
|
||||
symbol_str = symbol or ""
|
||||
amount_str = str(amount) if amount is not None else ""
|
||||
quantity_str = str(quantity) if quantity is not None else ""
|
||||
price_str = str(price) if price is not None else ""
|
||||
|
||||
# Create hash string with pipe delimiter
|
||||
hash_string = (
|
||||
f"{account_id}|"
|
||||
f"{run_date.isoformat()}|"
|
||||
f"{symbol_str}|"
|
||||
f"{action}|"
|
||||
f"{amount_str}|"
|
||||
f"{quantity_str}|"
|
||||
f"{price_str}"
|
||||
)
|
||||
|
||||
# Generate SHA-256 hash
|
||||
return hashlib.sha256(hash_string.encode("utf-8")).hexdigest()
|
||||
91
backend/app/utils/option_parser.py
Normal file
91
backend/app/utils/option_parser.py
Normal file
@@ -0,0 +1,91 @@
|
||||
"""Option symbol parsing utilities."""
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Optional, NamedTuple
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
class OptionInfo(NamedTuple):
|
||||
"""
|
||||
Parsed option information.
|
||||
|
||||
Attributes:
|
||||
underlying_symbol: Base ticker symbol (e.g., "AAPL")
|
||||
expiration_date: Option expiration date
|
||||
option_type: "CALL" or "PUT"
|
||||
strike_price: Strike price
|
||||
"""
|
||||
underlying_symbol: str
|
||||
expiration_date: datetime
|
||||
option_type: str
|
||||
strike_price: Decimal
|
||||
|
||||
|
||||
def parse_option_symbol(option_symbol: str) -> Optional[OptionInfo]:
|
||||
"""
|
||||
Parse Fidelity option symbol format into components.
|
||||
|
||||
Fidelity format: -SYMBOL + YYMMDD + C/P + STRIKE
|
||||
Example: -AAPL260116C150 = AAPL Call expiring Jan 16, 2026 at $150 strike
|
||||
|
||||
Args:
|
||||
option_symbol: Fidelity option symbol string
|
||||
|
||||
Returns:
|
||||
OptionInfo object if parsing successful, None otherwise
|
||||
|
||||
Examples:
|
||||
>>> parse_option_symbol("-AAPL260116C150")
|
||||
OptionInfo(
|
||||
underlying_symbol='AAPL',
|
||||
expiration_date=datetime(2026, 1, 16),
|
||||
option_type='CALL',
|
||||
strike_price=Decimal('150')
|
||||
)
|
||||
|
||||
>>> parse_option_symbol("-TSLA251219P500")
|
||||
OptionInfo(
|
||||
underlying_symbol='TSLA',
|
||||
expiration_date=datetime(2025, 12, 19),
|
||||
option_type='PUT',
|
||||
strike_price=Decimal('500')
|
||||
)
|
||||
"""
|
||||
# Regex pattern: -SYMBOL + YYMMDD + C/P + STRIKE
|
||||
# Symbol: one or more uppercase letters
|
||||
# Date: 6 digits (YYMMDD)
|
||||
# Type: C (call) or P (put)
|
||||
# Strike: digits with optional decimal point
|
||||
pattern = r"^-([A-Z]+)(\d{6})([CP])(\d+\.?\d*)$"
|
||||
|
||||
match = re.match(pattern, option_symbol)
|
||||
if not match:
|
||||
return None
|
||||
|
||||
symbol, date_str, option_type, strike_str = match.groups()
|
||||
|
||||
# Parse date (YYMMDD format)
|
||||
try:
|
||||
# Assume 20XX for years (works until 2100)
|
||||
year = 2000 + int(date_str[:2])
|
||||
month = int(date_str[2:4])
|
||||
day = int(date_str[4:6])
|
||||
expiration_date = datetime(year, month, day)
|
||||
except (ValueError, IndexError):
|
||||
return None
|
||||
|
||||
# Parse option type
|
||||
option_type_full = "CALL" if option_type == "C" else "PUT"
|
||||
|
||||
# Parse strike price
|
||||
try:
|
||||
strike_price = Decimal(strike_str)
|
||||
except (ValueError, ArithmeticError):
|
||||
return None
|
||||
|
||||
return OptionInfo(
|
||||
underlying_symbol=symbol,
|
||||
expiration_date=expiration_date,
|
||||
option_type=option_type_full,
|
||||
strike_price=strike_price,
|
||||
)
|
||||
Reference in New Issue
Block a user