145 lines
5.0 KiB
Python
145 lines
5.0 KiB
Python
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}
|