from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from pydantic import BaseModel import yfinance as yf from app.db.database import get_db from app.db.models import User, PortfolioItem from app.api.auth import get_current_user router = APIRouter() class BuyRequest(BaseModel): symbol: str quantity: int buy_price: float class SellRequest(BaseModel): portfolio_item_id: int @router.post("/buy") def buy_stock(req: BuyRequest, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)): final_buy_price = req.buy_price # If price wasn't provided (e.g. from watchlist), fetch it live if final_buy_price <= 0: try: ticker = yf.Ticker(req.symbol) final_buy_price = ticker.fast_info.last_price except Exception as e: print(f"Error fetching live price on buy for {req.symbol}: {e}") raise HTTPException(status_code=400, detail="Could not fetch live price, please try again.") new_item = PortfolioItem( user_id=current_user.id, symbol=req.symbol, quantity=req.quantity, buy_price=final_buy_price ) db.add(new_item) db.commit() db.refresh(new_item) return {"message": "Stock purchased successfully", "item": new_item} @router.get("/") def get_portfolio(db: Session = Depends(get_db), current_user: User = Depends(get_current_user)): items = db.query(PortfolioItem).filter(PortfolioItem.user_id == current_user.id).all() portfolio = [] total_invested = 0 total_current_value = 0 for item in items: # Fetch live price live_price = item.buy_price # Fallback try: ticker = yf.Ticker(item.symbol) live_price = ticker.fast_info.last_price except Exception as e: print(f"Error fetching price for {item.symbol}: {e}") invested = item.quantity * item.buy_price current_value = item.quantity * live_price pnl = current_value - invested pnl_percent = (pnl / invested) * 100 if invested > 0 else 0 total_invested += invested total_current_value += current_value portfolio.append({ "id": item.id, "symbol": item.symbol, "quantity": item.quantity, "buy_price": item.buy_price, "current_price": round(live_price, 2), "invested": round(invested, 2), "current_value": round(current_value, 2), "pnl": round(pnl, 2), "pnl_percent": round(pnl_percent, 2), "purchase_date": item.purchase_date }) total_pnl = total_current_value - total_invested total_pnl_percent = (total_pnl / total_invested) * 100 if total_invested > 0 else 0 return { "items": portfolio, "summary": { "total_invested": round(total_invested, 2), "total_current_value": round(total_current_value, 2), "total_pnl": round(total_pnl, 2), "total_pnl_percent": round(total_pnl_percent, 2) } } @router.delete("/{item_id}") def delete_portfolio_item(item_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)): item = db.query(PortfolioItem).filter(PortfolioItem.id == item_id, PortfolioItem.user_id == current_user.id).first() if not item: raise HTTPException(status_code=404, detail="Item not found") db.delete(item) db.commit() return {"message": "Stock sold/removed successfully"} @router.get("/recommendations/{symbol}") def get_recommendations(symbol: str, current_user: User = Depends(get_current_user)): try: ticker = yf.Ticker(symbol) recs = ticker.recommendations if recs is None or recs.empty: return {"error": "No recommendation data available", "confidence": 0} # Get the most recent month's data (period '0m' is usually index 0) latest = recs.iloc[0] strong_buy = int(latest.get("strongBuy", 0)) buy = int(latest.get("buy", 0)) hold = int(latest.get("hold", 0)) sell = int(latest.get("sell", 0)) strong_sell = int(latest.get("strongSell", 0)) total = strong_buy + buy + hold + sell + strong_sell if total == 0: return {"error": "No recommendations found", "confidence": 0} # Calculate confidence score # Strong Buy = 100, Buy = 75, Hold = 50, Sell = 25, Strong Sell = 0 score = (strong_buy * 100 + buy * 75 + hold * 50 + sell * 25) / total return { "symbol": symbol, "period": str(latest.get("period", "0m")), "strongBuy": strong_buy, "buy": buy, "hold": hold, "sell": sell, "strongSell": strong_sell, "total": total, "confidence": round(score, 1) } except Exception as e: print(f"Error fetching recommendations for {symbol}: {e}") return {"error": str(e), "confidence": 0}