Customize - Stock map list, watch list, home page
This commit is contained in:
14
backend/Dockerfile.backend
Normal file
14
backend/Dockerfile.backend
Normal file
@@ -0,0 +1,14 @@
|
||||
FROM python:3.13-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && apt-get install -y git cmake g++ && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
Binary file not shown.
Binary file not shown.
@@ -26,6 +26,9 @@ class ProfileUpdateRequest(BaseModel):
|
||||
gender: str = None
|
||||
password: str = None # Base64 encoded RSA-encrypted new password
|
||||
|
||||
class SectorMapUpdateRequest(BaseModel):
|
||||
sector_map: dict
|
||||
|
||||
@router.get("/public-key")
|
||||
def get_public_key():
|
||||
return {"public_key": public_key.decode("utf-8")}
|
||||
@@ -84,9 +87,16 @@ def get_me(current_user: User = Depends(get_current_user)):
|
||||
"display_name": current_user.display_name,
|
||||
"email_id": current_user.email_id,
|
||||
"mobile_no": current_user.mobile_no,
|
||||
"gender": current_user.gender
|
||||
"gender": current_user.gender,
|
||||
"sector_map": current_user.sector_map
|
||||
}
|
||||
|
||||
@router.put("/update-sectors")
|
||||
def update_sectors(req: SectorMapUpdateRequest, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
current_user.sector_map = req.sector_map
|
||||
db.commit()
|
||||
return {"message": "Sectors updated successfully"}
|
||||
|
||||
@router.put("/update-profile")
|
||||
def update_profile(req: ProfileUpdateRequest, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
|
||||
if req.display_name is not None:
|
||||
|
||||
@@ -4,24 +4,56 @@ from app.services.stock_engine import (
|
||||
get_scan, get_ribbon, get_smartmoney, get_trend, get_marketintel
|
||||
)
|
||||
|
||||
import requests
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@router.get("/scan")
|
||||
def scan(current_user = Depends(get_current_user)):
|
||||
return get_scan()
|
||||
return get_scan(current_user.sector_map or {})
|
||||
|
||||
@router.get("/ribbon")
|
||||
def ribbon(current_user = Depends(get_current_user)):
|
||||
return get_ribbon()
|
||||
return get_ribbon(current_user.sector_map or {})
|
||||
|
||||
@router.get("/smartmoney")
|
||||
def smartmoney(current_user = Depends(get_current_user)):
|
||||
return get_smartmoney()
|
||||
return get_smartmoney(current_user.sector_map or {})
|
||||
|
||||
@router.get("/trend")
|
||||
def trend(current_user = Depends(get_current_user)):
|
||||
return get_trend()
|
||||
return get_trend(current_user.sector_map or {})
|
||||
|
||||
@router.get("/marketintel")
|
||||
def marketintel(current_user = Depends(get_current_user)):
|
||||
return get_marketintel()
|
||||
|
||||
@router.get("/search")
|
||||
def search(q: str, current_user = Depends(get_current_user)):
|
||||
headers = {'User-Agent': 'Mozilla/5.0'}
|
||||
url = f"https://query2.finance.yahoo.com/v1/finance/search?q={q}"esCount=10&newsCount=0"
|
||||
res = requests.get(url, headers=headers)
|
||||
if res.status_code == 200:
|
||||
data = res.json()
|
||||
quotes = data.get("quotes", [])
|
||||
# Filter for Indian stocks (.NS or .BO)
|
||||
indian_stocks = [q for q in quotes if q.get("symbol", "").endswith((".NS", ".BO"))][:10]
|
||||
|
||||
if indian_stocks:
|
||||
import yfinance as yf
|
||||
|
||||
symbols = [q["symbol"] for q in indian_stocks]
|
||||
tickers = yf.Tickers(" ".join(symbols))
|
||||
|
||||
for stock in indian_stocks:
|
||||
try:
|
||||
info = tickers.tickers[stock["symbol"]].info
|
||||
stock["currentPrice"] = info.get("currentPrice") or info.get("regularMarketPrice")
|
||||
stock["marketCap"] = info.get("marketCap")
|
||||
except Exception as e:
|
||||
print(f"Error fetching {stock['symbol']}: {e}")
|
||||
stock["currentPrice"] = None
|
||||
stock["marketCap"] = None
|
||||
|
||||
return indian_stocks
|
||||
return []
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
|
||||
DATABASE_URL = "postgresql://postgres:M%40triXPostgr3s%406202@103.125.129.116:5333/stock_scanner"
|
||||
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://postgres:M%40triXPostgr3s%406202@host.docker.internal:5333/stock_scanner")
|
||||
SECRET_KEY = "stock-scanner-super-secret-key-12345"
|
||||
ALGORITHM = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 60
|
||||
|
||||
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
from sqlalchemy import Column, Integer, String
|
||||
from sqlalchemy import Column, Integer, String, JSON
|
||||
from app.db.database import Base
|
||||
|
||||
class User(Base):
|
||||
@@ -11,3 +11,4 @@ class User(Base):
|
||||
email_id = Column(String, nullable=True)
|
||||
mobile_no = Column(String, nullable=True)
|
||||
gender = Column(String, nullable=True)
|
||||
sector_map = Column(JSON, nullable=True)
|
||||
|
||||
Binary file not shown.
@@ -425,12 +425,12 @@ def analyze(stock):
|
||||
# =========================================
|
||||
# ✅ API - FAST SCAN
|
||||
# =========================================
|
||||
def get_scan():
|
||||
def get_scan(user_sector_map):
|
||||
|
||||
sector_output = {}
|
||||
all_stocks = []
|
||||
|
||||
for sector, stocks in sector_map.items():
|
||||
for sector, stocks in user_sector_map.items():
|
||||
|
||||
data = list(ThreadPoolExecutor(5).map(analyze, stocks))
|
||||
data = [x for x in data if x]
|
||||
@@ -536,26 +536,28 @@ def analyze_ribbon(stock):
|
||||
return None
|
||||
|
||||
|
||||
def get_ribbon():
|
||||
def get_ribbon(user_sector_map):
|
||||
|
||||
results = []
|
||||
|
||||
for stocks in sector_map.values():
|
||||
for sector, stocks in user_sector_map.items():
|
||||
data = list(ThreadPoolExecutor(5).map(analyze_ribbon, stocks))
|
||||
data = [x for x in data if x]
|
||||
for x in data:
|
||||
x["sector"] = sector
|
||||
results.extend(data)
|
||||
|
||||
return results
|
||||
|
||||
def get_smartmoney():
|
||||
def get_smartmoney(user_sector_map):
|
||||
|
||||
results = []
|
||||
|
||||
for sector, stocks in sector_map.items():
|
||||
|
||||
for sector, stocks in user_sector_map.items():
|
||||
data = list(ThreadPoolExecutor(5).map(analyze, stocks))
|
||||
data = [x for x in data if x]
|
||||
|
||||
for x in data:
|
||||
x["sector"] = sector
|
||||
results.extend(data)
|
||||
|
||||
|
||||
@@ -575,15 +577,15 @@ def get_smartmoney():
|
||||
"all": results
|
||||
}
|
||||
|
||||
def get_trend():
|
||||
def get_trend(user_sector_map):
|
||||
|
||||
results = []
|
||||
|
||||
for sector, stocks in sector_map.items():
|
||||
|
||||
for sector, stocks in user_sector_map.items():
|
||||
data = list(ThreadPoolExecutor(5).map(analyze, stocks))
|
||||
data = [x for x in data if x]
|
||||
|
||||
for x in data:
|
||||
x["sector"] = sector
|
||||
results.extend(data)
|
||||
|
||||
results.sort(
|
||||
|
||||
114
backend/requirements.txt
Normal file
114
backend/requirements.txt
Normal file
@@ -0,0 +1,114 @@
|
||||
alembic==1.18.5
|
||||
annotated-doc==0.0.4
|
||||
annotated-types==0.7.0
|
||||
anyio==4.12.1
|
||||
APScheduler==3.11.2
|
||||
argon2-cffi==25.1.0
|
||||
argon2-cffi-bindings==25.1.0
|
||||
bcrypt==5.0.0
|
||||
beautifulsoup4==4.15.0
|
||||
cachetools==7.0.5
|
||||
certifi==2026.1.4
|
||||
cffi==2.0.0
|
||||
charset-normalizer==3.4.7
|
||||
click==8.3.1
|
||||
cryptography==48.0.0
|
||||
curl_cffi==0.16.0
|
||||
defusedxml==0.7.1
|
||||
dnspython==2.8.0
|
||||
-e git+https://github.com/maddy23285/docvision.git@b0a6bea7ad1db9f515c9128964370c6bfdaad9e1#egg=docfingerprint
|
||||
-e git+https://github.com/maddy23285/docvision.git@b0a6bea7ad1db9f515c9128964370c6bfdaad9e1#egg=docfingerprint_embedding&subdirectory=embedding_service
|
||||
ecdsa==0.19.2
|
||||
email-validator==2.3.0
|
||||
fastapi==0.128.0
|
||||
filelock==3.17.0
|
||||
fonttools==4.62.1
|
||||
fpdf2==2.8.7
|
||||
fsspec==2025.2.0
|
||||
h11==0.16.0
|
||||
hf-xet==1.5.2
|
||||
httpcore==1.0.9
|
||||
httptools==0.7.1
|
||||
httpx==0.28.1
|
||||
huggingface_hub==0.36.2
|
||||
idna==3.11
|
||||
ImageHash==4.3.2
|
||||
imap-tools==1.13.0
|
||||
iniconfig==2.3.0
|
||||
Jinja2==3.1.4
|
||||
lxml==6.1.1
|
||||
Mako==1.3.12
|
||||
MarkupSafe==3.0.2
|
||||
mpmath==1.3.0
|
||||
multitasking==0.0.13
|
||||
networkx==3.4.2
|
||||
numpy==2.2.3
|
||||
ollama==0.6.2
|
||||
opencv-python==4.12.0.88
|
||||
opencv-python-headless==4.13.0.92
|
||||
packaging==26.2
|
||||
pandas==3.0.5
|
||||
passlib==1.7.4
|
||||
pdf2image==1.17.0
|
||||
pdfminer.six==20251230
|
||||
pdfplumber==0.11.9
|
||||
peewee==4.3.0
|
||||
pgvector==0.5.0
|
||||
pillow==11.1.0
|
||||
platformdirs==4.11.0
|
||||
pluggy==1.6.0
|
||||
protobuf==7.35.1
|
||||
psycopg==3.3.4
|
||||
psycopg-binary==3.3.4
|
||||
psycopg2-binary==2.9.11
|
||||
pyasn1==0.6.4
|
||||
pycparser==3.0
|
||||
pycryptodome==3.23.0
|
||||
pycryptodomex==3.21.0
|
||||
pydantic==2.12.5
|
||||
pydantic-settings==2.14.2
|
||||
pydantic_core==2.41.5
|
||||
Pygments==2.20.0
|
||||
PyJWT==2.11.0
|
||||
pypdf==6.13.0
|
||||
pypdfium2==5.9.0
|
||||
pytesseract==0.3.13
|
||||
pytest==8.4.2
|
||||
pytest-asyncio==0.26.0
|
||||
python-dateutil==2.9.0.post0
|
||||
python-docx==1.2.0
|
||||
python-dotenv==1.2.1
|
||||
python-jose==3.5.0
|
||||
python-multipart==0.0.22
|
||||
pytz==2026.3.post1
|
||||
PyWavelets==1.9.0
|
||||
PyYAML==6.0.3
|
||||
redis==5.2.0
|
||||
regex==2026.7.19
|
||||
requests==2.34.2
|
||||
rsa==4.9.1
|
||||
safetensors==0.8.0
|
||||
scipy==1.18.0
|
||||
setuptools==75.8.0
|
||||
six==1.17.0
|
||||
soupsieve==2.9.1
|
||||
SQLAlchemy==2.0.46
|
||||
starlette==0.50.0
|
||||
structlog==25.5.0
|
||||
sympy==1.13.1
|
||||
tokenizers==0.22.2
|
||||
torch==2.6.0
|
||||
torchaudio==2.6.0
|
||||
torchvision==0.21.0
|
||||
tqdm==4.69.0
|
||||
transformers==4.57.6
|
||||
typing-inspection==0.4.2
|
||||
typing_extensions==4.15.0
|
||||
tzlocal==5.3.1
|
||||
urllib3==2.7.0
|
||||
uvicorn==0.40.0
|
||||
uvloop==0.22.1
|
||||
watchfiles==1.1.1
|
||||
websockets==16.0
|
||||
yfinance==1.5.2
|
||||
zxing-cpp==2.3.0
|
||||
Reference in New Issue
Block a user