This commit is contained in:
@@ -18,6 +18,10 @@ const providers: Record<ProviderType, Provider> = {
|
||||
baseUrl:
|
||||
"https://www.deco.proteste.pt/investe/investimentos/fundos/${symbol}",
|
||||
},
|
||||
[ProviderType.FT]: {
|
||||
type: ProviderType.FT,
|
||||
baseUrl: "https://markets.ft.com/data/funds/tearsheet/summary?s=${symbol}",
|
||||
},
|
||||
};
|
||||
|
||||
interface SymbolConfig {
|
||||
@@ -67,6 +71,8 @@ async function getLatestPrice(symbol: string) {
|
||||
return await scrapeBPI(page, symbol, config);
|
||||
case ProviderType.DECO:
|
||||
return await scrapeDeco(page, symbol, config);
|
||||
case ProviderType.FT:
|
||||
return await scrapeFT(page, symbol, config);
|
||||
default:
|
||||
throw new Error(`Unsupported type: ${config.type}`);
|
||||
}
|
||||
@@ -155,6 +161,43 @@ async function scrapeBPI(page: Page, symbol: string, config: SymbolConfig) {
|
||||
};
|
||||
}
|
||||
|
||||
async function scrapeFT(page: Page, symbol: string, config: SymbolConfig) {
|
||||
// Interact with the DOM to retrieve the price and date
|
||||
const price = await page.evaluate(() => {
|
||||
const spanTags = document.getElementsByTagName("span");
|
||||
const priceSearchText = "Price (EUR)";
|
||||
let priceElementFound;
|
||||
|
||||
for (let i = 0; i < spanTags.length; i++) {
|
||||
if (spanTags[i].textContent.trim() == priceSearchText) {
|
||||
priceElementFound = spanTags[i];
|
||||
if (priceElementFound) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!priceElementFound) {
|
||||
throw new Error("Could not find price element");
|
||||
}
|
||||
|
||||
return priceElementFound?.nextSibling?.textContent;
|
||||
});
|
||||
|
||||
const marketPrice = parseFloat(
|
||||
price?.replace("€", "").trim().replace(",", ".") || "0",
|
||||
);
|
||||
|
||||
return {
|
||||
symbol,
|
||||
name: config.name,
|
||||
currency: "EUR",
|
||||
price: marketPrice,
|
||||
// date in FT is not available, so we use the current date as a placeholder
|
||||
// yyyy-mm-dd format
|
||||
date: new Date().toISOString().split("T")[0],
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
// API endpoint to get latest price for a symbol
|
||||
app.get("/price/:symbol", async (req, res) => {
|
||||
const { symbol } = req.params;
|
||||
@@ -186,6 +229,7 @@ app.get("/", (req, res) => {
|
||||
health: "/health",
|
||||
"price-bpi": "/price/bpi/:symbol",
|
||||
"price-deco": "/price/deco/:symbol",
|
||||
"price-ft": "/price/ft/:symbol",
|
||||
},
|
||||
example: `/price/bpi/BPIDEST2040`,
|
||||
});
|
||||
@@ -194,5 +238,6 @@ app.get("/", (req, res) => {
|
||||
// Start the server
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server running on http://localhost:${PORT}`);
|
||||
console.log("Available symbols:", Object.keys(availableSymbols).join(", "));
|
||||
console.log(`Example: http://localhost:${PORT}/price/bpi/BPIDEST2040`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user