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%
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