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:
151
backend/app/api/endpoints/accounts.py
Normal file
151
backend/app/api/endpoints/accounts.py
Normal file
@@ -0,0 +1,151 @@
|
||||
"""Account management API endpoints."""
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List
|
||||
|
||||
from app.api.deps import get_db
|
||||
from app.models import Account
|
||||
from app.schemas import AccountCreate, AccountUpdate, AccountResponse
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("", response_model=AccountResponse, status_code=status.HTTP_201_CREATED)
|
||||
def create_account(account: AccountCreate, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Create a new brokerage account.
|
||||
|
||||
Args:
|
||||
account: Account creation data
|
||||
db: Database session
|
||||
|
||||
Returns:
|
||||
Created account
|
||||
|
||||
Raises:
|
||||
HTTPException: If account number already exists
|
||||
"""
|
||||
# Check if account number already exists
|
||||
existing = (
|
||||
db.query(Account)
|
||||
.filter(Account.account_number == account.account_number)
|
||||
.first()
|
||||
)
|
||||
|
||||
if existing:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"Account with number {account.account_number} already exists",
|
||||
)
|
||||
|
||||
# Create new account
|
||||
db_account = Account(**account.model_dump())
|
||||
db.add(db_account)
|
||||
db.commit()
|
||||
db.refresh(db_account)
|
||||
|
||||
return db_account
|
||||
|
||||
|
||||
@router.get("", response_model=List[AccountResponse])
|
||||
def list_accounts(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
||||
"""
|
||||
List all accounts.
|
||||
|
||||
Args:
|
||||
skip: Number of records to skip
|
||||
limit: Maximum number of records to return
|
||||
db: Database session
|
||||
|
||||
Returns:
|
||||
List of accounts
|
||||
"""
|
||||
accounts = db.query(Account).offset(skip).limit(limit).all()
|
||||
return accounts
|
||||
|
||||
|
||||
@router.get("/{account_id}", response_model=AccountResponse)
|
||||
def get_account(account_id: int, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Get account by ID.
|
||||
|
||||
Args:
|
||||
account_id: Account ID
|
||||
db: Database session
|
||||
|
||||
Returns:
|
||||
Account details
|
||||
|
||||
Raises:
|
||||
HTTPException: If account not found
|
||||
"""
|
||||
account = db.query(Account).filter(Account.id == account_id).first()
|
||||
|
||||
if not account:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Account {account_id} not found",
|
||||
)
|
||||
|
||||
return account
|
||||
|
||||
|
||||
@router.put("/{account_id}", response_model=AccountResponse)
|
||||
def update_account(
|
||||
account_id: int, account_update: AccountUpdate, db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Update account details.
|
||||
|
||||
Args:
|
||||
account_id: Account ID
|
||||
account_update: Updated account data
|
||||
db: Database session
|
||||
|
||||
Returns:
|
||||
Updated account
|
||||
|
||||
Raises:
|
||||
HTTPException: If account not found
|
||||
"""
|
||||
db_account = db.query(Account).filter(Account.id == account_id).first()
|
||||
|
||||
if not db_account:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Account {account_id} not found",
|
||||
)
|
||||
|
||||
# Update fields
|
||||
update_data = account_update.model_dump(exclude_unset=True)
|
||||
for field, value in update_data.items():
|
||||
setattr(db_account, field, value)
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_account)
|
||||
|
||||
return db_account
|
||||
|
||||
|
||||
@router.delete("/{account_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def delete_account(account_id: int, db: Session = Depends(get_db)):
|
||||
"""
|
||||
Delete an account and all associated data.
|
||||
|
||||
Args:
|
||||
account_id: Account ID
|
||||
db: Database session
|
||||
|
||||
Raises:
|
||||
HTTPException: If account not found
|
||||
"""
|
||||
db_account = db.query(Account).filter(Account.id == account_id).first()
|
||||
|
||||
if not db_account:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=f"Account {account_id} not found",
|
||||
)
|
||||
|
||||
db.delete(db_account)
|
||||
db.commit()
|
||||
Reference in New Issue
Block a user