SPB Git

spb/vquant Public MIT

VibeQuant — AI-powered institutional-grade financial intelligence platform.

TypeScript 84.3% Python 11.7% JavaScript 1.6% CSS 1.5% HTML 0.7%
12.1 KB · 334 lines typescript
Raw Blame History
1/*2 * =============================================================================3 *  VibeQuant (vquant) — AI-Powered Financial Intelligence Platform4 * -----------------------------------------------------------------------------5 *  File:      server/routes/fmp.ts6 *7 *  Author:    Simon-Pierre Boucher8 *  Contact:   contact@spboucher.ai9 *  Website:   https://www.spboucher.ai10 *  Demo:      https://www.vquant.ai11 *  License:   MIT (see LICENSE)12 *13 *  Copyright © 2026 Simon-Pierre Boucher. All rights reserved.14 * =============================================================================15 */1617import type { Express, Request, Response } from "express";18import {19  getCompanyProfile,20  getIncomeStatement,21  getBalanceSheet,22  getCashFlowStatement,23  getKeyMetrics,24  getFinancialRatios,25  getStockQuote,26  getHistoricalPrice,27  getFinancialNews,28  getAnalystEstimates,29  getPriceTarget,30  getPriceTargetSummary,31  getUpgradesDowngrades,32  getInstitutionalHolders,33  getESGScore,34  getInsiderTrading,35  getEarningsSurprises,36  getDividendHistory,37  getIntradayPrice,38  getMarketHours,39  getGainers,40  getLosers,41  getActives,42} from "../services/fmpService";43import { logger } from "../utils/logger";4445export function registerFmpRoutes(app: Express) {46  // ─── Market Overview ────────────────────────────────────4748  app.get("/api/fmp/market-hours", async (_req: Request, res: Response) => {49    try {50      res.json(await getMarketHours());51    } catch (error) {52      logger.error("Get market hours error:", error);53      res.status(500).json({ error: "Failed to fetch market hours" });54    }55  });5657  app.get("/api/fmp/gainers", async (_req: Request, res: Response) => {58    try {59      res.json(await getGainers());60    } catch (error) {61      logger.error("Get gainers error:", error);62      res.status(500).json({ error: "Failed to fetch gainers" });63    }64  });6566  app.get("/api/fmp/losers", async (_req: Request, res: Response) => {67    try {68      res.json(await getLosers());69    } catch (error) {70      logger.error("Get losers error:", error);71      res.status(500).json({ error: "Failed to fetch losers" });72    }73  });7475  app.get("/api/fmp/actives", async (_req: Request, res: Response) => {76    try {77      res.json(await getActives());78    } catch (error) {79      logger.error("Get actives error:", error);80      res.status(500).json({ error: "Failed to fetch most active stocks" });81    }82  });8384  // ─── Stock Quotes & Prices ──────────────────────────────8586  app.get("/api/fmp/quote/:symbol", async (req: Request, res: Response) => {87    try {88      res.json(await getStockQuote(req.params.symbol));89    } catch (error) {90      logger.error("Get quote error:", error);91      res.status(500).json({ error: "Failed to fetch stock quote" });92    }93  });9495  app.get("/api/fmp/company-profile/:symbol", async (req: Request, res: Response) => {96    try {97      res.json(await getCompanyProfile(req.params.symbol));98    } catch (error) {99      logger.error("Get company profile error:", error);100      res.status(500).json({ error: "Failed to fetch company profile" });101    }102  });103104  app.get("/api/fmp/stock-quote/:symbol", async (req: Request, res: Response) => {105    try {106      res.json(await getStockQuote(req.params.symbol));107    } catch (error) {108      logger.error("Get stock quote error:", error);109      res.status(500).json({ error: "Failed to fetch stock quote" });110    }111  });112113  app.get("/api/fmp/historical-price/:symbol", async (req: Request, res: Response) => {114    try {115      res.json(await getHistoricalPrice(req.params.symbol));116    } catch (error) {117      logger.error("Get historical price error:", error);118      res.status(500).json({ error: "Failed to fetch historical price" });119    }120  });121122  app.get("/api/fmp/intraday/:symbol", async (req: Request, res: Response) => {123    try {124      const interval = (req.query.interval as string) || '5min';125      logger.info(`Fetching intraday data for ${req.params.symbol} with interval ${interval}`);126      res.json(await getIntradayPrice(req.params.symbol, interval));127    } catch (error) {128      logger.error("Get intraday price error:", error);129      res.status(500).json({ error: "Failed to fetch intraday price data" });130    }131  });132133  // ─── Financial Statements ──────────────────────────────134135  app.get("/api/fmp/income-statement/:symbol", async (req: Request, res: Response) => {136    try {137      const period = (req.query.period as 'annual' | 'quarter') || 'annual';138      const limit = parseInt(req.query.limit as string) || 5;139      res.json(await getIncomeStatement(req.params.symbol, period, limit));140    } catch (error) {141      logger.error("Get income statement error:", error);142      res.status(500).json({ error: "Failed to fetch income statement" });143    }144  });145146  app.get("/api/fmp/balance-sheet/:symbol", async (req: Request, res: Response) => {147    try {148      const period = (req.query.period as 'annual' | 'quarter') || 'annual';149      const limit = parseInt(req.query.limit as string) || 5;150      res.json(await getBalanceSheet(req.params.symbol, period, limit));151    } catch (error) {152      logger.error("Get balance sheet error:", error);153      res.status(500).json({ error: "Failed to fetch balance sheet" });154    }155  });156157  app.get("/api/fmp/cash-flow/:symbol", async (req: Request, res: Response) => {158    try {159      const period = (req.query.period as 'annual' | 'quarter') || 'annual';160      const limit = parseInt(req.query.limit as string) || 5;161      res.json(await getCashFlowStatement(req.params.symbol, period, limit));162    } catch (error) {163      logger.error("Get cash flow error:", error);164      res.status(500).json({ error: "Failed to fetch cash flow statement" });165    }166  });167168  app.get("/api/fmp/key-metrics/:symbol", async (req: Request, res: Response) => {169    try {170      const period = (req.query.period as 'annual' | 'quarter') || 'annual';171      const limit = parseInt(req.query.limit as string) || 5;172      res.json(await getKeyMetrics(req.params.symbol, period, limit));173    } catch (error) {174      logger.error("Get key metrics error:", error);175      res.status(500).json({ error: "Failed to fetch key metrics" });176    }177  });178179  app.get("/api/fmp/financial-ratios/:symbol", async (req: Request, res: Response) => {180    try {181      const period = (req.query.period as 'annual' | 'quarter') || 'annual';182      const limit = parseInt(req.query.limit as string) || 5;183      res.json(await getFinancialRatios(req.params.symbol, period, limit));184    } catch (error) {185      logger.error("Get financial ratios error:", error);186      res.status(500).json({ error: "Failed to fetch financial ratios" });187    }188  });189190  // ─── Analysis & Estimates ──────────────────────────────191192  app.get("/api/fmp/analyst-estimates/:symbol", async (req: Request, res: Response) => {193    try {194      const period = (req.query.period as 'annual' | 'quarter') || 'annual';195      res.json(await getAnalystEstimates(req.params.symbol, period));196    } catch (error) {197      logger.error("Get analyst estimates error:", error);198      res.status(500).json({ error: "Failed to fetch analyst estimates" });199    }200  });201202  app.get("/api/fmp/price-target/:symbol", async (req: Request, res: Response) => {203    try {204      res.json(await getPriceTarget(req.params.symbol));205    } catch (error) {206      logger.error("Get price target error:", error);207      res.status(500).json({ error: "Failed to fetch price targets" });208    }209  });210211  app.get("/api/fmp/price-target-summary/:symbol", async (req: Request, res: Response) => {212    try {213      res.json(await getPriceTargetSummary(req.params.symbol));214    } catch (error) {215      logger.error("Get price target summary error:", error);216      res.status(500).json({ error: "Failed to fetch price target summary" });217    }218  });219220  app.get("/api/fmp/upgrades-downgrades/:symbol", async (req: Request, res: Response) => {221    try {222      res.json(await getUpgradesDowngrades(req.params.symbol));223    } catch (error) {224      logger.error("Get upgrades downgrades error:", error);225      res.status(500).json({ error: "Failed to fetch upgrades downgrades" });226    }227  });228229  app.get("/api/fmp/earnings-surprises/:symbol", async (req: Request, res: Response) => {230    try {231      res.json(await getEarningsSurprises(req.params.symbol));232    } catch (error) {233      logger.error("Get earnings surprises error:", error);234      res.status(500).json({ error: "Failed to fetch earnings surprises" });235    }236  });237238  // ─── Other Data ─────────────────────────────────────────239240  app.get("/api/fmp/dividend-history/:symbol", async (req: Request, res: Response) => {241    try {242      res.json(await getDividendHistory(req.params.symbol));243    } catch (error) {244      logger.error("Get dividend history error:", error);245      res.status(500).json({ error: "Failed to fetch dividend history" });246    }247  });248249  app.get("/api/fmp/institutional-holders/:symbol", async (req: Request, res: Response) => {250    try {251      res.json(await getInstitutionalHolders(req.params.symbol));252    } catch (error) {253      logger.error("Get institutional holders error:", error);254      res.status(500).json({ error: "Failed to fetch institutional holders" });255    }256  });257258  app.get("/api/fmp/financial-news/:symbol", async (req: Request, res: Response) => {259    try {260      const limit = parseInt(req.query.limit as string) || 10;261      res.json(await getFinancialNews(req.params.symbol, limit));262    } catch (error) {263      logger.error("Get financial news error:", error);264      res.status(500).json({ error: "Failed to fetch financial news" });265    }266  });267268  app.get("/api/fmp/insider-trading/:symbol", async (req: Request, res: Response) => {269    try {270      const limit = parseInt(req.query.limit as string) || 20;271      res.json(await getInsiderTrading(req.params.symbol, limit));272    } catch (error) {273      logger.error("Get insider trading error:", error);274      res.status(500).json({ error: "Failed to fetch insider trading" });275    }276  });277278  app.get("/api/fmp/esg-score/:symbol", async (req: Request, res: Response) => {279    try {280      res.json(await getESGScore(req.params.symbol));281    } catch (error) {282      logger.error("Get ESG score error:", error);283      res.status(500).json({ error: "Failed to fetch ESG score" });284    }285  });286287  // ─── Chart Data ─────────────────────────────────────────288289  app.get("/api/fmp/chart/light/:symbol", async (req: Request, res: Response) => {290    try {291      const { from, to } = req.query;292      const result = await import('../services/fmpService').then(m =>293        m.getChartLight(req.params.symbol, from as string, to as string),294      );295      res.json(result);296    } catch (error) {297      logger.error("Get chart light error:", error);298      res.status(500).json({ error: "Failed to fetch chart data" });299    }300  });301302  app.get("/api/fmp/chart/full/:symbol", async (req: Request, res: Response) => {303    try {304      const { from, to } = req.query;305      const result = await import('../services/fmpService').then(m =>306        m.getChartFull(req.params.symbol, from as string, to as string),307      );308      res.json(result);309    } catch (error) {310      logger.error("Get chart full error:", error);311      res.status(500).json({ error: "Failed to fetch chart data" });312    }313  });314315  app.get("/api/fmp/chart/intraday/:symbol", async (req: Request, res: Response) => {316    try {317      const { interval, from, to, nonadjusted } = req.query;318      const result = await import('../services/fmpService').then(m =>319        m.getChartIntraday(320          req.params.symbol,321          interval as '1min' | '5min' | '15min' | '30min' | '1hour' | '4hour',322          from as string,323          to as string,324          nonadjusted === 'true',325        ),326      );327      res.json(result);328    } catch (error) {329      logger.error("Get chart intraday error:", error);330      res.status(500).json({ error: "Failed to fetch intraday chart data" });331    }332  });333}334