# Project export: FinBot

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: UC Berkeley AI Hackathon 2025
- Tagline: FinBot: Personal AI Financial Assistant AI-powered dashboard for real-time insights, portfolio analysis, and smart investment advice—driven by a Claude multi-agent chatbot.
- Devpost: https://devpost.com/software/finbot-5pzjic
- GitHub: https://github.com/Pprarthana0808/FinBot
- Team: 1 GitHub contributor(s) — Pprarthana0808 (2 commits)

## Devpost submission (written by the team)

### Inspiration

Managing personal finances is extremely time-consuming, fragmented, and overwhelming for many. And managing your wealth shouldn’t feel like a second job. We built FinBot to simplify that experience — empowering users with a cohesive AI-powered financial dashboard, real-time data and trends, and a multi-agent financial chatbot available 24/7. Our goal is to help everyday people take control of their financial future with confidence and clarity.

### What it does

FinBot is a full-stack AI-powered financial platform that helps users gain complete visibility and control over their personal finances. By uploading financial documents such as bank statements, investment portfolios, and real estate records, users can build a cohesive dashboard that tracks both assets and liabilities in real time. The platform provides tailored investment insights, stock trend predictions powered by machine learning, and personalized news updates based on each user’s financial interests. FinBot’s intelligent multi-agent system, backed by Claude, delivers high-quality financial analysis and recommendations, while the integrated chatbot and voice assistant offer 24/7 support across every page of the app. Whether users are looking to monitor their wealth, evaluate opportunities, or make informed decisions.

### How we built it

We built FinBot as a full-stack AI financial advisor platform using a React frontend, providing a chatbot across the app. The backend is powered by Node.js and Express, managing routes for PDF parsing, Claude-powered agent interactions, and more machine learning services. We integrated pdf2json to extract data from uploaded financial documents and used Claude 3 Haiku via Anthropic’s API to power our multi-agent analysis system. The custom ClaudeClient module ensures consistent formatting and prompt control across all LLM requests. To enhance decision support, we included Python-based ML scripts for stock trend forecasting and price prediction, triggered via child processes. Financial news sentiment is derived from external APIs and analyzed using NLP techniques.

### Challenges we ran into

One of the biggest challenges we faced was enforcing structured, concise outputs from Claude while also having a high standard of financial reasoning. Prompt tuning required iterative refinement to ensure the multi-agent system delivered readable and scannable insights without redundant language. Moreover, integrating multiple input modes, like PDF uploads, voice input, and typed queries, into one seamless user experience was complex; it tested the coordination between the frontend and backend. Parsing unstructured financial documents reliably was another challenge requiring handling of any malformed or dense PDFs. Despite these challenges, we were able to build a cohesive and intelligent financial assistant by combining modular design with targeted system prompts and robust backend logic.

### Accomplishments we're proud of

We are proud of building a complete AI financial advisor platform. FinBot seamlessly integrates PDF-parsing, multi-agent reasoning, machine learning, and real-time conversation into a single, cohesive user experience. We successfully created a modular Claude-powered agent system that delivers structured, insightful financial analysis across diverse input types—including voice, PDFs, and text. Our ability to extract and interpret unstructured financial data, generate meaningful recommendations, and present them through a clean, user-friendly dashboard is a major technical and design achievement. We're especially proud of what FinBot of what we achieved, which is empowering users to understand and take control of their finances with tools that are intelligent, personal, and always available.

### What we learned

While building FinBot, we gained a deep understanding of how to effectively orchestrate large language models like Claude within a multi-agent architecture. We learned how critical prompt engineering is for accuracy, consistency, clarity, and tone across responses. Working with unstructured financial documents taught us the challenges of data normalization and the importance of preprocessing before feeding data to an LLM. Additionally, we also learned how to integrate different technologies, like PDF parsers and voice recognition, into ML prediction scripts and RESTful APIs. Most importantly, we significantly deepened our knowledge of finance, including numerous key concepts. Understanding how to interpret and communicate these metrics accurately was essential to building FinBot.

### What's next

We envision FinBot becoming a household financial app—America's AI-powered financial app for anyone looking to better understand, manage, and grow their wealth. Our next steps include integrating with bank accounts, stock, and ETF platforms to enable automatic syncing of accounts, portfolios, and transactions. We plan to expand the Claude-powered agent system with specialized agents for budgeting, tax optimization, retirement planning, and more. Additionally, we aim to enhance personalization with user financial profiles, customizable alerts, family accounts, and learning-based insights that adapt over time. Eventually, we want FinBot to serve as a fully interactive and proactive financial coach.

## README (from the GitHub repository)

# FinBot

## Detected evidence (automated analysis)

Indexed codebase: 15 recognized source files, 23 KB.
- Express (technology) — detected in the code
- Firebase (technology) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- Ollama (technology) — claimed on Devpost, not found in the code
- React (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (100 of 100)

```
backend/.env
backend/agents/claude_agents.js
backend/app.py
backend/controllers/chatController.js
backend/data/users.json
backend/fetch_aapl.py
backend/fetch_price_prediction.py
backend/fetch_trend_forecast.py
backend/history.json
backend/index.js
backend/package.json
backend/predict_trend.py
backend/routes/analysis.js
backend/routes/authRoutes.js
backend/routes/chat.js
backend/server.js
backend/summarize_llm.js
backend/uploads/022c6725bbcce6287f8e4a92635b0f3d
backend/uploads/02ac18977bf9a7a30fbc1eb5d98bec5e
backend/uploads/04361c33d5a8b4ce1124f81861eacbd2
backend/uploads/07f4ea27c2422f95a61b843a3c06b5f8
backend/uploads/08df740fdabe9e6aee3828766bf0f148
backend/uploads/095bc0544eeda348057c269619c35351
backend/uploads/09c9c4662aa6d9a00a0c12a1c65c4dbb
backend/uploads/0ada25ea71c61210d61ef6bdc0e561e0
backend/uploads/0c63dddf2fd218a237d6e300daa614ea
backend/uploads/0e45a258cf44898d36aee4e04bf3f6d7
backend/uploads/100060078084b79e95efc668820e148b
backend/uploads/12223fcf3e38e00c05a523c7659fa988
backend/uploads/136d2531f0662965c66aa0adf879f239
backend/uploads/13bd5070fe15ba5e4bf9603157cf0443
backend/uploads/1a899d06d70390d097234af9e628680c
backend/uploads/1c594b6371b31189445f2359f90c48f5
backend/uploads/206cb860d3e138cf5dfda367871098cd
backend/uploads/213cb52ee6420d3c7a512a42157426ef
backend/uploads/239aeb687fa0afcef91f9343fdbc561a
backend/uploads/265333c965424d82dd8bfcc12a321a3e
backend/uploads/26939b38f96b764132ae834b69f9ef16
backend/uploads/2867f441079cbb168a473468b1893395
backend/uploads/2cda3e73b547d411336d9fb78f99eeab
backend/uploads/2de248c78e33b31b52463d2b58e051ea
backend/uploads/3131650b53e33b23e1ffbbde863230ac
backend/uploads/39c852014435bff17b9aeaff2ce66d8a
backend/uploads/403be0a9e0007bef971cf0241b689e8d
backend/uploads/4662264f8f3a0b69e921ead1141bbb07
backend/uploads/49b9fba36e56e87067114214a95881ac
backend/uploads/4a023b0d81114fc52a32c5a74bac11e9
backend/uploads/4ef67006adce071e72c734e465d9aa10
backend/uploads/5d2ed575e84e60b57ac36c22e0dfae22
backend/uploads/5fd32215d1dc19921f82cd5bb04a0f90
backend/uploads/6013e48dfd0e0330fbcff2718e9ee749
backend/uploads/60578175debae2559b42868575ad0e99
backend/uploads/61e4fab35392ee4a71125a3c696cfdcc
backend/uploads/6368022e8af89fb98f02a4007688e537
backend/uploads/63bff4f8789308f7aa8e7df85a14205b
backend/uploads/648e26e7c0c7fa37d84dd345b5839429
backend/uploads/6ad5c8a65ac094c62010c4e4ae34ac5d
backend/uploads/6b3401548ae5c8d9cd9144c6320b7c36
backend/uploads/6d08e41cf37ccf655187a2223e626e0c
backend/uploads/7403a35dcbf09474c22fca4cfca3d103
backend/uploads/7aa877b3713f4fb50b5f3d6aec074389
backend/uploads/7dfcceb9359d67fb501d9b057dbee7d3
backend/uploads/899172a165f4f77adfc49889086dea68
backend/uploads/8b86c7084c79a9c721e819993fff83d6
backend/uploads/8c326e717ca2560967c2d69c878ef60c
backend/uploads/8c3a1dae2fbbccbef05f282a1fa6aa3e
backend/uploads/8f0e9d236687341c20e0dc9667148119
backend/uploads/90111be9248dd9175623643fb9d36696
backend/uploads/9763e085c6ef9811cd1afc2a4cbd301c
backend/uploads/98edcce88ad64d85c4eb4f4de1098ecc
backend/uploads/9e7d66b641bd5ed4ba8c4a2027280bb2
backend/uploads/a6bff9c18ac1c48b0761a204c43f4a89
backend/uploads/a7de96b1a4388dd26b90e25417283fbe
backend/uploads/a7e8276e75998a853ed0bb344290a840
backend/uploads/ab5861d1b8dcec98b6a96a154e996c5d
backend/uploads/b7b52250e35e8a3c08d354b5aceeeb8b
backend/uploads/b85b81396bb022a0f86cbf9861ece15c
backend/uploads/bfed9c3442b690677b8f27fde712a1ff
backend/uploads/c9efe8030d5d2879047cd1d46d9a1dc7
backend/uploads/d0fc4920369b4f6993862dd7b9c5739f
backend/uploads/d115c33fb04c8d95d488402801f86e40
backend/uploads/d18cb8083356d65dd4c6c278c9389f6b
backend/uploads/d194b7a4dad646cf799faa809560e42a
backend/uploads/df66d12c65f42ae5978b63499b2822ab
backend/uploads/e0acb945149ba7e5af3545dc02d0e328
backend/uploads/e236dfc8dcaebcd5fbe8f9e9119f9ee5
backend/uploads/e5039f9c0870fe6d1e22ae97ed767148
backend/uploads/e5c232b7ac489b2077fd97787281edb3
backend/uploads/e75bacc07ac8ea9f98f33f37a0c9da94
backend/uploads/e84634538f89f1c8d4fe6bcdd7cf0c94
backend/uploads/f25c285f0b426a61ddb884b4e8fd0a5c
backend/uploads/f3ce1227cb0b651536dbff91b33737b3
backend/uploads/f6407a0933b6e12ab2cf8032ea22f3f1
backend/uploads/f85decf40092b8a27c2c9478e2145755
backend/uploads/fc81f503de398bbf9203ac150a1878cc
backend/uploads/ff075c6797f70cd8c2a44bcba117ddf3
backend/uploads/ff71d27d1bd7d3d48a9ede9652b07461
backend/utils/ClaudeClient.js
package.json
README.md
```

### Dependencies

- backend/package.json: cors@^2.8.5, dotenv@^16.5.0, express@^4.21.2, express-session@^1.18.1, multer@^2.0.1, node-fetch@^2.7.0, nodemailer@^7.0.3, pdf-parse@^1.1.1, pdf2json@^3.1.6, python-shell@^5.0.0, yahoo-finance2@^2.13.3
- package.json: axios@^1.10.0, chart.js@^4.5.0, cors@^2.8.5, express@^5.1.0, firebase@^11.9.1, react-chartjs-2@^5.3.0, react-simple-maps@^3.0.0, sentiment@^5.0.2, yahoo-finance2@^2.13.3

### Recent commits (newest first)

- Your commit message
- Initial commit

## Key source files (fetched from GitHub, selected and truncated for size)

### package.json

```
{
  "dependencies": {
    "axios": "^1.10.0",
    "chart.js": "^4.5.0",
    "cors": "^2.8.5",
    "express": "^5.1.0",
    "firebase": "^11.9.1",
    "react-chartjs-2": "^5.3.0",
    "react-simple-maps": "^3.0.0",
    "sentiment": "^5.0.2",
    "yahoo-finance2": "^2.13.3"
  }
}

```

### backend/package.json

```
{
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "name": "backend",
  "version": "1.0.0",
  "main": "index.js",
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "cors": "^2.8.5",
    "dotenv": "^16.5.0",
    "express": "^4.21.2",
    "express-session": "^1.18.1",
    "multer": "^2.0.1",
    "node-fetch": "^2.7.0",
    "nodemailer": "^7.0.3",
    "pdf-parse": "^1.1.1",
    "pdf2json": "^3.1.6",
    "python-shell": "^5.0.0",
    "yahoo-finance2": "^2.13.3"
  },
  "description": ""
}

```

### backend/index.js

```javascript
const express = require('express');
const cors = require('cors');
const yahooFinance = require('yahoo-finance2').default;

const app = express();
const PORT = 5001;

app.use(cors());

app.get('/api/stocks', async (req, res) => {
  const symbols = req.query.symbols?.split(',') || ['AAPL', 'TSLA', 'AMZN'];

  try {
    const results = await Promise.all(
      symbols.map((symbol) => yahooFinance.quote(symbol))
    );

    res.json(results.map(({ symbol, regularMarketPrice, shortName }) => ({
      symbol,
      name: shortName,
      price: regularMarketPrice
    })));
  } catch (error) {
    res.status(500).json({ error: 'Failed to fetch stock data' });
  }
});

app.listen(PORT, () => {
  console.log(`FinBot backend running at http://localhost:${PORT}`);
});

```

### backend/app.py

```python
from flask import Flask, jsonify
from flask_cors import CORS
import yfinance as yf

app = Flask(__name__)
CORS(app)

def get_price_data(ticker):
    try:
        stock = yf.Ticker(ticker)
        hist = stock.history(period="1mo")

        if hist.empty:
            return {"error": f"No historical data found for '{ticker}'."}

        info = stock.info
        if not info or "regularMarketPrice" not in info:
            return {"error": f"Ticker '{ticker}' may be invalid or missing market data."}

        close_prices = hist['Close'].round(2).tolist()
        labels = hist.index.strftime('%b %d').tolist()

        return {
            "prices": close_prices,
            "labels": labels,
            "recommendation": info.get("recommendationKey", "HOLD").upper(),
            "targetPrice": round(info.get("targetMeanPrice", 0), 2),
            "confidence": 73,
            "currentPrice": round(info.get("regularMarketPrice", 0), 2),
            "change": round(info.get("regularMarketChange", 0), 2),
            "high": round(info.get("fiftyTwoWeekHigh", 0), 2),
            "low": round(info.get("fiftyTwoWeekLow", 0), 2),
            "volume": info.get("volume", 0)
        }

    except Exception as e:
        return {"error": f"Backend error for '{ticker}': {str(e)}"}



def get_sentiment_data(ticker):
    # Simulate sentiment score
    return {
        "overallScore": 0.65,
        "twitter": { "bullish": 70, "bearish": 30 },
        "reddit": { "bullish": 60, "bearish": 40 }
    }

-
def get_prediction_data(ticker):
    return {
        "currentPrice": 150,
        "predictedPrice": 165,
        "confidence": 82,
        "forecast": [150, 152, 155, 158, 162, 164, 165],
        "features": { "momentum": 40, "volume": 20, "RSI": 25, "news": 15 },
        "metrics": { "mae": 1.8, "r2": 0.89, "directionalAccuracy": 80 }
    }


def get_options_strategy(ticker):
    return {
        "strategy": "Covered Call",
        "strikePrice": 155,
        "premium": 2.5,
        "returnPotential": 4.5,
        "riskLevel": "Moderate",
        "impliedVolatility": 22.3,
        "expiry": "2025-07-19",
        "pros": [
            "Generates premium income",
            "Reduces break-even cost",
            "Simple and conservative strategy"
        ],
        "cons": [
            "Limits upside potential",
            "Requires holding underlying shares",
            "May require margin account"
        ]
    }


@app.route('/api/stock/<ticker>/price')
def price_route(ticker):
    print(f"📡 Fetching price data for {ticker}")
    return jsonify(get_price_data(ticker.upper()))

@app.route('/api/stock/<ticker>/sentiment')
def sentiment_route(ticker):
    return jsonify(get_sentiment_data(ticker.upper()))

@app.route('/api/stock/<ticker>/prediction')
def prediction_route(ticker):
    return jsonify(get_prediction_data(ticker.upper()))

@app.route('/api/stock/<ticker>/options')
def options_route(ticker):
    return jsonify(get_options_strategy(ticker.upper()))


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

```

### backend/server.js

```javascript
const express = require('express');
const cors = require('cors');
const axios = require('axios');
const Sentiment = require('sentiment');
const { spawn } = require('child_process');
const path = require('path');
const multer = require('multer');
const fs = require('fs');
const PDFParser = require('pdf2json');
const summarizeFinancialContent = require('./summarize_llm');
const analysisRoutes = require('./routes/analysis');

require('dotenv').config();
console.log('🔐 CLAUDE_API_KEY loaded:', process.env.CLAUDE_API_KEY ? '✅ yes' : '❌ no');

const app = express();
app.use(cors());
app.use(express.json());
app.use(analysisRoutes); 

const sentiment = new Sentiment();
const API_KEY = process.env.TWELVE_API_KEY;
const NEWS_API_KEY = process.env.NEWS_API_KEY;
const CLAUDE_API_KEY = process.env.CLAUDE_API_KEY;

const HISTORY_FILE = 'history.json';
let history = fs.existsSync(HISTORY_FILE)
  ? JSON.parse(fs.readFileSync(HISTORY_FILE))
  : [];

const upload = multer({ dest: 'uploads/' });

app.post('/api/parse-pdf', upload.single('file'), async (req, res) => {
  if (!req.file) return res.status(400).json({ error: 'No file uploaded' });

  const pdfParser = new PDFParser();
  pdfParser.on("pdfParser_dataError", errData =>
    res.status(500).json({ error: errData.parserError })
  );

  pdfParser.on("pdfParser_dataReady", async pdfData => {
    const rawText = [];
    pdfData?.formImage?.Pages.forEach((page) => {
      const pageText = page.Texts.map(textObj => {
        const text = decodeURIComponent(textObj.R[0].T);
        return text;
      }).join(' ');
      rawText.push(pageText);
    });
    const content = rawText.join('\n');

    try {
      const summary = await summarizeFinancialContent(content);
      summary.contentSnippet = content.slice(0, 1000) + '...';
      res.json(summary);
    } catch (error) {
      console.error('❌ LLM summarization error:', error.message);
      res.status(500).json({ error: 'LLM summarization failed' });
    }
  });

  pdfParser.loadPDF(req.file.path);
});

app.post('/api/store-summary', (req, res) => {
  const { name, summary } = req.body;
  if (name && summary) {
    history.push({ name, summary });
    fs.writeFileSync(HISTORY_FILE, JSON.stringify(history, null, 2));
    res.json({ status: 'stored' });
  } else {
    res.status(400).json({ error: 'Missing name or summary' });
  }
});

app.get('/api/get-history', (req, res) => {
  res.json(history);
});

app.delete('/api/clear-history', (req, res) => {
  history = [];
  fs.writeFileSync(HISTORY_FILE, JSON.stringify([]));
  res.json({ status: 'cleared' });
});

app.get('/api/stock', async (req, res) => {
  let symbol = req.query.symbol?.toUpperCase();
  if (!symbol || !/^[A-Z.]+$/.test(symbol)) symbol = 'AAPL';

  try {
    const url = `https://api.twelvedata.com/time_series?symbol=${symbol}&interval=1day&outputsize=30&apikey=${API_KEY}`;
    const response = await axios.get(url);
    if (!response.data || !response.data.values) return res.status(502).json({ error: 'Invalid data from Twelve Data' });

    const series = response.data.values;
    const dates = series.map(entry => entry.datetime).reverse();
    const prices = series.map(entry => parseFloat(entry.close)).reverse();
    res.json({ dates, prices });
  } catch (err) {
    console.error('❌ Stock API error:', err.message);
    res.status(500).json({ error: 'Failed to fetch stock data' });
  }
});

app.get('/api/sentiment', async (req, res) => {
  const { symbol = 'AAPL' } = req.query;
  if (!NEWS_API_KEY) return res.status(500).json({ error: 'Missing NEWS_API_KEY in .env' });

  try {
    const news = await axios.get(`https://newsapi.org/v2/everything?q=${symbol}&pageSize=10&language=en&apiKey=${NEWS_API_KEY}`);
    const articles = news.data.articles || [];
    const scores = { positive: 0, negative: 0, neutral: 0, total: 0 };

    articles.forEach(article => {
      const text = `${article.title} ${article.description || ''}`;
      const result = sentiment.analyze(text);
      if (result.score > 0) scores.positive++;
      else if (result.score < 0) scores.negative++;
      else scores.neutral++;
      scores.total += result.score;
    });

    const overall = scores.total / (scores.positive + scores.negative + scores.neutral || 1);
    res.json({ score: Number(overall.toFixed(2)), ...scores });
  } catch (err) {
    console.error('❌ Sentiment API error:', err.message);
    res.status(500).json({ error: 'Failed to fetch sentiment data' });
  }
});

app.get('/api/predict', async (req, res) => {
    const { symbol = 'AAPL' } = req.query;
    const scriptPath = path.join(__dirname, 'fetch_price_prediction.py');
  
    const py = spawn('python3', [scriptPath, symbol]);
    let data = '', error = '';
  
    py.stdout.on('data', chunk => {
      const text = chunk.toString();
      console.log('📈 Python stdout:', text);
      data += text;
    });
  
    py.stderr.on('data', chunk => { error += chunk.toString(); });
  
    py.on('close', code => {
      if (error) console.error('❌ Python error:', error);
      if (code !== 0) return res.status(500).json({ error: 'Python script failed' });
  
      try {
        const match = data.trim().match(/\[.*\]|\{.*\}/s);
        if (!match) throw new Error('No valid JSON found in Python output');
  
        const output = JSON.parse(match[0]);
        res.json(output);
      } catch (err) {
        console.error('❌ JSON parse error:', err.message);
        res.status(500).json({ error: 'Invalid JSON from Python' });
      }
    });
  });
  
app.post('/api/ask', async (req, res) => {
    const { question } = req.body;
    if (!question) return res.status(400).json({ error: 'Question is required' });
    if (!CLAUDE_API_KEY) return res.status(500).json({ error: 'Missing CLAUDE_API_KEY in .env' });
  
    try {
      const response = await fetch('https://api.anthropic.com/v1/messages', {
        method: 'POST',
        headers: {
          'x-api-key': CLAUDE_API_KEY,
          'anthropic-version': '2023-06-01'
[truncated — 2434 more characters]
```

### backend/fetch_trend_forecast.py

```python
import sys
import json
import random

def forecast(symbol):
    outcome = random.choices(
        ["rise", "crash", "neutral"],
        weights=[0.5, 0.2, 0.3],
        k=1
    )[0]

    confidence = {
        "rise": round(random.uniform(60, 85), 1),
        "crash": round(random.uniform(50, 75), 1),
        "neutral": round(random.uniform(40, 60), 1)
    }[outcome]

    return {
        "symbol": symbol.upper(),
        "trend": outcome,
        "confidence": confidence
    }

if __name__ == "__main__":
    symbol = sys.argv[1] if len(sys.argv) > 1 else "AAPL"
    result = forecast(symbol)
    print(json.dumps(result), flush=True)

```

### backend/fetch_aapl.py

```python

import sys
import json

def predict(symbol):
    base_prices = {
        "AAPL": 150.0,
        "AMZN": 125.0,
        "TSLA": 220.0,
        "GOOGL": 135.0
    }

    base = base_prices.get(symbol.upper(), 100.0)
    prediction = [round(base * (1 + 0.01 * (i + 1)), 2) for i in range(7)]

    return {
        "symbol": symbol.upper(),
        "currentPrice": base,
        "prediction": prediction,
        "confidence": 75,
        "features": {
            "momentum": 40,
            "volume": 25,
            "indicators": 25,
            "sentiment": 10
        },
        "metrics": {
            "mae": 1.3,
            "r2": 0.91,
            "accuracy": 87
        }
    }

if __name__ == "__main__":
    symbol = sys.argv[1] if len(sys.argv) > 1 else "AAPL"
    result = predict(symbol)
    print(json.dumps(result))  

```

### backend/fetch_price_prediction.py

```python
import sys
import json
from datetime import datetime, timedelta

def predict(symbol):
    base_prices = {
        "AAPL": 150.00,
        "AMZN": 125.00,
        "TSLA": 220.00,
        "GOOGL": 135.00
    }

    base = base_prices.get(symbol.upper(), 100.00)
    prediction = []

    for i in range(7):
        date = (datetime.today() + timedelta(days=i)).strftime('%Y-%m-%d')
        mean = round(base * (1 + 0.01 * (i + 1)), 2)
        upper = round(mean * 1.02, 2)
        lower = round(mean * 0.98, 2)
        prediction.append({
            "date": date,
            "mean": mean,
            "upper": upper,
            "lower": lower
        })

    return prediction

if __name__ == "__main__":
    input_symbol = sys.argv[1] if len(sys.argv) > 1 else "AAPL"
    result = predict(input_symbol)
    json.dump(result, sys.stdout)  

```

### backend/predict_trend.py

```python
import sys
import json
import numpy as np
import pandas as pd
from tensorflow.keras.models import load_model
from sklearn.preprocessing import MinMaxScaler
import yfinance as yf
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

symbol = sys.argv[1]
model = load_model(f'models/{symbol}_lstm.h5')  # Pretrained model per symbol

a
data = yf.download(symbol, period='6mo')
close_prices = data['Close'].values.reshape(-1, 1)


scaler = MinMaxScaler()
scaled = scaler.fit_transform(close_prices)

X_test = []
days = 60
for i in range(days, len(scaled)):
    X_test.append(scaled[i - days:i, 0])

X_test = np.array(X_test)
X_test = np.reshape(X_test, (X_test.shape[0], X_test.shape[1], 1))

last_sequence = X_test[-1].reshape(1, 60, 1)
predicted_price = model.predict(last_sequence)[0][0]
current_price = scaled[-1][0]

trend = 'rise' if predicted_price > current_price else 'fall'
confidence = abs(predicted_price - current_price)

print(json.dumps({
    "prediction": trend,
    "confidence": round(float(confidence), 2)
}))

```

### backend/summarize_llm.js

```javascript
const axios = require('axios');

async function summarizeFinancialContent(text) {
  const prompt = `
You are a financial parser. Given the following PDF content, extract structured information.

Return a JSON object with:
{
  type: "401k" | "bank" | "unknown",
  provider: string,
  accountNumber?: string,
  balance?: string,
  employeeContributions?: string,
  employerContributions?: string,
  endingValue?: string,
  allocation?: {
    Stocks: number,
    Bonds: number,
    Cash: number
  },
  transactions?: string[]
}

Content:
${text}
`;

  const res = await axios.post('http://localhost:11434/api/generate', {
    model: 'mistral',
    prompt,
    stream: false
  });

  const outputText = res.data.response;

  try {
    const jsonStart = outputText.indexOf('{');
    const jsonEnd = outputText.lastIndexOf('}');
    const cleanJson = outputText.slice(jsonStart, jsonEnd + 1);
    return JSON.parse(cleanJson);
  } catch (err) {
    console.error("❌ Failed to parse LLM output:", outputText);
    return { type: 'unknown', error: 'LLM failed to extract fields' };
  }
}

module.exports = summarizeFinancialContent;

```

[6 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]