Files
marketscanner/backend/app/api/endpoints/watchlist.py
mindesbunister 074787f067 Initial project structure: MarketScanner - Fear-to-Fortune Trading Intelligence
Features:
- FastAPI backend with stocks, news, signals, watchlist, analytics endpoints
- React frontend with TailwindCSS dark mode trading dashboard
- Celery workers for news fetching, sentiment analysis, pattern detection
- TimescaleDB schema for time-series stock data
- Docker Compose setup for all services
- OpenAI integration for sentiment analysis
2026-01-08 14:15:51 +01:00

142 lines
4.2 KiB
Python

"""
Watchlist API Endpoints
"""
from typing import List, Optional
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.core.database import get_db
from app.models.watchlist import Watchlist
from app.models.stock import Stock
from app.schemas.watchlist import WatchlistResponse, WatchlistCreate, WatchlistUpdate
router = APIRouter()
@router.get("/", response_model=List[WatchlistResponse])
async def list_watchlist(
db: AsyncSession = Depends(get_db),
priority: Optional[int] = Query(None, ge=1, le=3, description="Filter by priority"),
):
"""Get all items in watchlist."""
query = select(Watchlist).where(Watchlist.is_active == True)
if priority:
query = query.where(Watchlist.priority == priority)
query = query.order_by(Watchlist.priority)
result = await db.execute(query)
return result.scalars().all()
@router.post("/", response_model=WatchlistResponse)
async def add_to_watchlist(
item: WatchlistCreate,
db: AsyncSession = Depends(get_db),
):
"""Add a stock to the watchlist."""
# Find the stock
stock_query = select(Stock).where(Stock.symbol == item.symbol.upper())
stock_result = await db.execute(stock_query)
stock = stock_result.scalar_one_or_none()
if not stock:
raise HTTPException(status_code=404, detail=f"Stock {item.symbol} not found")
# Check if already in watchlist
existing = await db.execute(
select(Watchlist).where(Watchlist.stock_id == stock.id)
)
if existing.scalar_one_or_none():
raise HTTPException(
status_code=400,
detail=f"Stock {item.symbol} is already in watchlist"
)
watchlist_item = Watchlist(
stock_id=stock.id,
panic_alert_threshold=item.panic_alert_threshold,
price_alert_low=item.price_alert_low,
price_alert_high=item.price_alert_high,
priority=item.priority,
notes=item.notes,
)
db.add(watchlist_item)
await db.commit()
await db.refresh(watchlist_item)
return watchlist_item
@router.put("/{watchlist_id}", response_model=WatchlistResponse)
async def update_watchlist_item(
watchlist_id: UUID,
update: WatchlistUpdate,
db: AsyncSession = Depends(get_db),
):
"""Update a watchlist item."""
query = select(Watchlist).where(Watchlist.id == watchlist_id)
result = await db.execute(query)
item = result.scalar_one_or_none()
if not item:
raise HTTPException(status_code=404, detail="Watchlist item not found")
update_data = update.model_dump(exclude_unset=True)
for key, value in update_data.items():
setattr(item, key, value)
await db.commit()
await db.refresh(item)
return item
@router.delete("/{watchlist_id}")
async def remove_from_watchlist(
watchlist_id: UUID,
db: AsyncSession = Depends(get_db),
):
"""Remove a stock from the watchlist."""
query = select(Watchlist).where(Watchlist.id == watchlist_id)
result = await db.execute(query)
item = result.scalar_one_or_none()
if not item:
raise HTTPException(status_code=404, detail="Watchlist item not found")
await db.delete(item)
await db.commit()
return {"message": "Removed from watchlist"}
@router.delete("/symbol/{symbol}")
async def remove_symbol_from_watchlist(
symbol: str,
db: AsyncSession = Depends(get_db),
):
"""Remove a stock from watchlist by symbol."""
# Find the stock
stock_query = select(Stock).where(Stock.symbol == symbol.upper())
stock_result = await db.execute(stock_query)
stock = stock_result.scalar_one_or_none()
if not stock:
raise HTTPException(status_code=404, detail=f"Stock {symbol} not found")
# Find and remove watchlist item
watchlist_query = select(Watchlist).where(Watchlist.stock_id == stock.id)
watchlist_result = await db.execute(watchlist_query)
item = watchlist_result.scalar_one_or_none()
if not item:
raise HTTPException(
status_code=404,
detail=f"Stock {symbol} is not in watchlist"
)
await db.delete(item)
await db.commit()
return {"message": f"Removed {symbol} from watchlist"}