frontend/src/lib/utils.ts
2025-03-01 12:45:32 +01:00

1802 lines
49 KiB
TypeScript

import { formatDistanceToNow } from "date-fns";
let pbCloudImage = import.meta.env.VITE_IMAGE_POCKETBASE_URL; // Set a default API URL
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
import { cubicOut } from "svelte/easing";
import type { TransitionConfig } from "svelte/transition";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
type FlyAndScaleParams = {
y?: number;
x?: number;
start?: number;
duration?: number;
};
export function removeCompanyStrings(name) {
const wordsToRemove = ["Technologies", "Inc.","Corp.","Corporation","Holding","Limited","Group"];
if (!name) return "";
return wordsToRemove?.reduce((acc, word) => acc.replace(word, "").trim(), name);
}
export function convertToSlug(title) {
// Remove punctuation, hyphens, and special characters
const cleanedTitle = title
.replace(/[-.,?!:"]/g, "") // Remove punctuation and hyphens
.replace(/\s+/g, " ") // Replace multiple spaces with a single space
.trim(); // Remove leading and trailing spaces
// Convert to lowercase, split by spaces, and join with hyphens
const words = cleanedTitle.toLowerCase().split(" ");
const truncatedWords = words;
// Join with hyphens
const slug = truncatedWords.join("-");
return slug;
}
export function isPWAInstalled() {
try {
// For iOS (Safari)
const isInStandaloneMode = window.navigator.standalone;
// For Android and other platforms
const isStandalone = window.matchMedia("(display-mode: standalone)").matches;
return isInStandaloneMode || isStandalone;
}
catch(e) {
console.log(e)
return false;
}
}
export const computeGrowthSingleList = (data, actualList) => {
// Initialize the result list
let resultList = [];
for (let i = 0; i < data?.length; i++) {
const currentData = data[i];
// Find the corresponding actual data from one FY back
const correspondingActual = actualList?.find(
(entry) => Number(entry.FY) === Number(currentData.FY) - 1,
);
// Calculate growth if a matching entry exists in actualList
let growth = null;
if (
correspondingActual &&
correspondingActual?.val !== null &&
currentData.val !== null
) {
growth = (
((currentData?.val - correspondingActual?.val) /
Math.abs(correspondingActual?.val)) *
100
)?.toFixed(2);
}
// Push the result for this FY
resultList.push({
FY: currentData.FY,
val: currentData.val,
growth: growth !== null ? Number(growth) : null, // Convert growth to number or leave as null
});
}
return resultList;
}
export const compareTimes = (time1, time2) => {
const [hours1, minutes1] = time1.split(":").map(Number);
const [hours2, minutes2] = time2.split(":").map(Number);
if (hours1 > hours2) return 1;
if (hours1 < hours2) return -1;
if (minutes1 > minutes2) return 1;
if (minutes1 < minutes2) return -1;
return 0;
}
export const formatTime = (timeString) => {
// Split the time string into components
const [hours, minutes, seconds] = timeString.split(":").map(Number);
// Determine AM or PM
const period = hours >= 12 ? "PM" : "AM";
// Convert hours from 24-hour to 12-hour format
const formattedHours = hours % 12 || 12; // Converts 0 to 12 for midnight
// Format the time string
const formattedTimeString = `${formattedHours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")} ${period}`;
return formattedTimeString;
}
export const groupScreenerRules = (allRows) => {
const categoryOrder = [
"Most Popular", "Company Info","Price & Volume", "Options Activity","Valuation & Ratios", "Valuation & Price Targets", "Margins",
"Performance","Technical Analysis","Forecasts, Analysts & Price Targets", "Dividends", "Revenue / Sales", "Net Income", "Other Profits","Cash Flow", "Debt", "Shares Statistics", "Short Selling Statistics", "Others"
];
// Group rows by category
const grouped = allRows.reduce((acc, row) => {
// Ensure category is an array if it's a single string
const categories = Array.isArray(row.category) ? row.category : [row.category || "Others"]; // Default to "Others" if no category is provided
categories.forEach((category) => {
if (!acc[category]) {
acc[category] = [];
}
acc[category].push(row);
});
return acc;
}, {});
// Sort categories based on the defined order
const orderedGroupedRules = Object.fromEntries(
Object.entries(grouped).sort(
([keyA], [keyB]) => categoryOrder.indexOf(keyA) - categoryOrder.indexOf(keyB)
)
);
return orderedGroupedRules;
}
export const groupEarnings = (earnings) => {
return Object?.entries(
earnings?.reduce((acc, item) => {
// Force formatting in UTC to avoid local timezone conversion
const dateKey = new Intl.DateTimeFormat('en-US', {
day: '2-digit',
month: 'short',
year: 'numeric',
timeZone: 'UTC'
})?.format(new Date(item.date));
if (!acc[dateKey]) acc[dateKey] = [];
acc[dateKey].push(item);
return acc;
}, {})
)
// Sort the grouped dates in descending order
?.sort(([dateA], [dateB]) => new Date(dateA) - new Date(dateB))
?.map(([date, earnings]) => [
date,
// Sort earnings within the date by time (also treat times as UTC)
earnings?.sort((a, b) => {
const timeA = new Date(`1970-01-01T${a?.time}Z`);
const timeB = new Date(`1970-01-01T${b?.time}Z`);
return timeB - timeA;
})
]);
};
export const groupNews = (news, watchList) => {
return Object.entries(
news
?.map(item => {
// Add 'type' based on watchList
const match = watchList?.find(w => w?.symbol === item?.symbol);
return match ? { ...item, type: match.type } : { ...item };
})
?.reduce((acc, item) => {
// Use UTC to format the published date without shifting the day
const dateKey = new Intl.DateTimeFormat('en-US', {
day: '2-digit',
month: 'short',
year: 'numeric',
timeZone: 'UTC'
}).format(new Date(item?.publishedDate));
const titleKey = item?.title;
if (!acc[dateKey]) acc[dateKey] = {};
if (!acc[dateKey][titleKey]) acc[dateKey][titleKey] = [];
acc[dateKey][titleKey]?.push(item);
return acc;
}, {})
)
// Sort the grouped dates in descending order (latest date first)
?.sort(([dateA], [dateB]) => new Date(dateB) - new Date(dateA))
?.map(([date, titleGroup]) => [
date,
Object.entries(titleGroup)
// Sort titles by the latest time of their items
?.sort(([, itemsA], [, itemsB]) => {
const latestTimeA = new Date(Math.max(...itemsA?.map(item => new Date(item.publishedDate + 'Z'))));
const latestTimeB = new Date(Math.max(...itemsB?.map(item => new Date(item.publishedDate + 'Z'))));
return latestTimeB - latestTimeA;
})
?.map(([title, items]) => {
// Sort items within each title group by the latest time (treating times as UTC)
items.sort((a, b) => new Date(b?.publishedDate + 'Z') - new Date(a?.publishedDate + 'Z'));
// Get the unique symbols
const symbols = [...new Set(items?.map(item => item.symbol))];
return { title, items, symbols };
}),
]);
};
export const calculateChange = (oldList = [], newList = []) => {
if (!oldList.length || !newList.length) return [...oldList];
// Create a Map for fast lookups of new list items by symbol
const newListMap = new Map(newList.map((item) => [item.symbol, item]));
return oldList.map((item) => {
const newItem = newListMap.get(item.symbol);
// Check if the symbols match and the newItem has the necessary properties
if (newItem && newItem.symbol === item.symbol && newItem.avgPrice) {
const { price, changesPercentage } = item;
const newPrice = newItem.avgPrice;
// Only update the changesPercentage if both price and changesPercentage are defined
if (price != null && changesPercentage != null) {
const baseLine = price / (1 + Number(changesPercentage) / 100);
item.changesPercentage = ((newPrice / baseLine - 1) * 100);
}
item.previous = price;
item.price = newPrice;
}
return item;
});
};
export function updateStockList(stockList = [], originalData = []) {
// Create a Map for fast O(1) lookups of original data by symbol
const originalDataMap = new Map(originalData.map(item => [item.symbol, item]));
// Initialize an array to store the updated stock list
const updatedStockList = [];
// Iterate through each stock in the stockList
for (let i = 0; i < stockList.length; i++) {
const stock = stockList[i];
const matchingStock = originalDataMap?.get(stock?.symbol);
// If a matching stock is found, update it
if (matchingStock) {
updatedStockList.push({
...stock,
price: matchingStock.price,
changesPercentage: matchingStock.changesPercentage,
previous: matchingStock.previous ?? null,
});
} else {
// If no match, add the stock unchanged
updatedStockList.push(stock);
}
}
// Return the updated stock list
return updatedStockList;
}
export const flyAndScale = (
node: Element,
params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 0 },
): TransitionConfig => {
const style = getComputedStyle(node);
const transform = style.transform === "none" ? "" : style.transform;
const scaleConversion = (
valueA: number,
scaleA: [number, number],
scaleB: [number, number],
) => {
const [minA, maxA] = scaleA;
const [minB, maxB] = scaleB;
const percentage = (valueA - minA) / (maxA - minA);
const valueB = percentage * (maxB - minB) + minB;
return valueB;
};
const styleToString = (
style: Record<string, number | string | undefined>,
): string => {
return Object.keys(style).reduce((str, key) => {
if (style[key] === undefined) return str;
return str + key + ":" + style[key] + ";";
}, "");
};
return {
duration: Math.max(params.duration ?? 300, 1), // Ensure a minimum duration of 1ms
delay: 0,
css: (t) => {
const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
return styleToString({
transform:
transform +
"translate3d(" +
x +
"px, " +
y +
"px, 0) scale(" +
scale +
")",
opacity: t,
});
},
easing: cubicOut,
};
};
export const sortTableData = (key, displayList, rawData, sortOrders) => {
// Reset all other keys to 'none' except the current key
for (const k in sortOrders) {
if (k !== key) {
sortOrders[k].order = "none";
}
}
// Cycle through 'none', 'asc', 'desc' for the clicked key
const orderCycle = ["none", "asc", "desc"];
const originalData = rawData?.slice(0, 40);
const currentOrderIndex = orderCycle.indexOf(sortOrders[key].order);
sortOrders[key].order =
orderCycle[(currentOrderIndex + 1) % orderCycle.length];
const sortOrder = sortOrders[key].order;
// Reset to original data when 'none' and stop further sorting
if (sortOrder === "none") {
analytRatingList = [...originalData]; // Reset to original data (spread to avoid mutation)
return;
}
// Define a generic comparison function
const compareValues = (a, b) => {
const { type } = sortOrders[key];
let valueA, valueB;
switch (type) {
case "date":
valueA = new Date(a[key]);
valueB = new Date(b[key]);
break;
case "string":
valueA = a[key].toUpperCase();
valueB = b[key].toUpperCase();
return sortOrder === "asc"
? valueA.localeCompare(valueB)
: valueB.localeCompare(valueA);
case "number":
default:
valueA = parseFloat(a[key]);
valueB = parseFloat(b[key]);
break;
}
if (sortOrder === "asc") {
return valueA < valueB ? -1 : valueA > valueB ? 1 : 0;
} else {
return valueA > valueB ? -1 : valueA < valueB ? 1 : 0;
}
};
// Sort using the generic comparison function
analytRatingList = [...originalData].sort(compareValues);
};
export const formatDateRange = (lastDateStr) => {
// Convert lastDateStr to Date object
const lastDate = new Date(lastDateStr);
// Set the first date to the beginning of the month of lastDate
const firstDate = new Date(lastDate.getFullYear(), lastDate.getMonth(), 1);
// Format first and last dates
const firstDateFormatted = firstDate.toLocaleString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
day: "2-digit",
});
const lastDateFormatted = lastDate.toLocaleString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
day: "2-digit",
});
// Construct and return the formatted date range string
return `${firstDateFormatted} - ${lastDateFormatted}`;
};
export const serializeNonPOJOs = (obj) => {
return structuredClone(obj);
};
export const generateUsername = (name) => {
const randomHex = Math.floor(Math.random() * 65536).toString(16);
const id = randomHex.padStart(4, "0");
return `${name.slice(0, 5)}${id}`;
};
export const getImageURL = (collectionId, recordId, fileName, size = "0x0") => {
//For development or local storage or S3 bucket without CDN Cloudfront
if (pbCloudImage === "http://localhost:8090") {
return `${pbCloudImage}/api/files/${collectionId}/${recordId}/${fileName}?thumb=${size}`;
}
return `${pbCloudImage}/${collectionId}/${recordId}/${fileName}?thumb=${size}`;
};
export const validateData = async (formData, schema) => {
const body = Object.fromEntries(formData);
try {
const data = schema.parse(body);
return {
formData: data,
errors: null,
};
} catch (err) {
console.log("Error: ", err);
const errors = err.flatten();
return {
formData: body,
errors,
};
}
};
export function sumQuarterlyResultsByYear(quarterlyResults, namingList) {
const yearlySummaries = {};
const quarterCounts = {};
// FMP sucks since these keys are up to date only by the last quarter value
const lastQuarterKeys = new Set([namingList]); // Keys that need last quarter values
// Define a Set of keys to exclude from summing
// FMP sucks since these keys are up to date for every quarter hence no summation required
const excludeKeys = new Set([
"totalDebt",
"priceToEarnings",
"weightedAverageShsOut",
"weightedAverageShsOutDil",
]);
// Function to get the quarter number from the period string
function getQuarterNumber(period) {
switch (period) {
case "Q1":
return 1;
case "Q2":
return 2;
case "Q3":
return 3;
case "Q4":
return 4;
default:
return 0;
}
}
// Iterate over each quarterly result
quarterlyResults?.forEach((quarter) => {
// Extract year and quarter from the data
const year = quarter?.calendarYear;
const quarterNum = getQuarterNumber(quarter?.period);
// Initialize the year in summaries and quarter counts if not already present
if (!yearlySummaries[year]) {
yearlySummaries[year] = {
calendarYear: `${year}`, // Use end of the year date
lastQuarterProcessed: 0, // Keep track of the last quarter processed
date: quarter?.date, // Copy the 'date' field unchanged
};
quarterCounts[year] = 0;
}
// Increment the quarter count for the year
quarterCounts[year]++;
// Update last quarter processed if the current quarter is greater
if (quarterNum > yearlySummaries[year].lastQuarterProcessed) {
yearlySummaries[year].lastQuarterProcessed = quarterNum;
// Update the date to the latest quarter's date if applicable
yearlySummaries[year].date = quarter?.date;
}
// Sum up the numeric fields for the year, excluding specific keys
Object?.keys(quarter)?.forEach((key) => {
if (
typeof quarter[key] === "number" &&
!excludeKeys?.has(key) &&
!lastQuarterKeys.has(key)
) {
yearlySummaries[year][key] =
(yearlySummaries[year][key] || 0) + quarter[key];
} else if (excludeKeys.has(key)) {
// Directly copy the last quarter value for these keys
yearlySummaries[year][key] = quarter[key];
} else if (lastQuarterKeys.has(key) && quarterNum === 4) {
// Update only if it's the last quarter of the year
yearlySummaries[year][key] = quarter[key];
}
});
});
// Filter out years with less than 4 quarters
const validYears = Object?.keys(quarterCounts)?.filter(
(year) => quarterCounts[year] === 4,
);
const annualResults = validYears?.map((year) => yearlySummaries[year]);
// Sort the results by year in descending order
annualResults.sort((a, b) => b?.calendarYear?.localeCompare(a?.calendarYear));
return annualResults;
}
export const sortPostsByDate = (posts) => {
return posts.sort(function (a, b) {
return new Date(b.created) - new Date(a.created);
});
};
export function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
export function formatString(inputString) {
// Split the input string into words using space as a delimiter
const words = inputString?.split(" ");
// Capitalize the first letter of each word and convert the rest to lowercase
const formattedWords = words?.map((word, index) => {
if (word.length > 0) {
// Check if the word is "LTG" or "LLC" and keep them uppercase
const excludedList = [
"n/a",
"CEO",
"U.S.",
"NGL",
"ETF",
"SPDR",
"S&P",
"USD",
"US",
"JPMORGAN",
"SRS",
"AQR",
"XN",
"TIG",
"HAP",
"AB",
"AKRE",
"LTG",
"LLC",
"JVL",
"NJ",
"FMR",
"LP",
"NNS",
"BPS",
"BNP",
"PCG",
"CTC",
"IMC",
"PLC",
"WIT",
];
// Check if the word is "black-rock" and format it accordingly
if (
index < words?.length - 1 &&
word?.toLowerCase() === "black" &&
words[index + 1]?.toLowerCase() === "rock"
) {
return "Black Rock";
} else if (excludedList?.includes(word)) {
return word;
} else {
return word?.charAt(0)?.toUpperCase() + word?.slice(1)?.toLowerCase();
}
}
return word; // Handle empty words if any
});
// Join the formatted words back together with spaces
const formattedString = formattedWords?.join(" ");
return formattedString;
}
export function abbreviateNumberWithColor(number, addDollarSign = false, color = false) {
// Check if number is null or undefined, return "-" if true
if (number == null) {
return "-";
}
const negative = number < 0;
// Handle special case for exactly 1000
if (Math.abs(number) === 1000) {
const suffix = color ? '<span class=\"text-yellow-500\">K</span>' : 'K';
return addDollarSign
? negative
? `-\$1${suffix}`
: `\$1${suffix}`
: negative
? `-1${suffix}`
: `1${suffix}`;
}
if (Math.abs(number) !== 0 && Math.abs(number) > 1000) {
const suffixes = ["", "K", "M", "B", "B", "T", "Q", "Qu", "S", "O", "N", "D"];
const magnitude = Math.floor(Math.log10(Math.abs(number)));
let index = Math.min(Math.floor(magnitude / 3), suffixes.length - 1);
// Special case to keep numbers in trillions formatted as billions
if (index >= 4) {
index = 3; // Keep the suffix at "B"
}
let abbreviation = Math.abs(number) / Math.pow(10, index * 3);
// Set the desired number of decimals
if (abbreviation >= 1000) {
abbreviation = abbreviation.toFixed(1);
index++;
} else {
abbreviation = abbreviation.toFixed(2);
}
abbreviation = parseFloat(abbreviation).toLocaleString("en-US", {
maximumFractionDigits: 2,
minimumFractionDigits: 2,
});
let suffix = suffixes[index];
if (color) {
if (suffix === "K") {
suffix = '<span class="font-semibold text-[#8F82FE]">K</span>';
} else if (suffix === "M") {
suffix = '<span class="font-semibold text-[#C8A32D]">M</span>';
} else if (suffix === "B") {
suffix = '<span class="font-semibold text-[#2CB8A6]">B</span>';
}
}
const formattedNumber = abbreviation + suffix;
return addDollarSign
? (negative ? "-\$" : "\$") + formattedNumber
: negative
? "-" + formattedNumber
: formattedNumber;
} else if (Math.abs(number) >= 0 && Math.abs(number) < 1000) {
return addDollarSign
? (negative ? "-\$" : "\$") + Math.abs(number)
: negative
? "-" + Math.abs(number)
: number.toString();
} else {
return addDollarSign ? "\$0" : "0";
}
}
export function abbreviateNumber(
number,
addDollarSign = false,
stripTrailingZeros = true
) {
// Check if number is null or undefined, return "-" if true
if (number == null) {
return "-";
}
const negative = number < 0;
// Handle special case for exactly 1000
if (Math.abs(number) === 1000) {
return addDollarSign
? negative
? "-$1K"
: "$1K"
: negative
? "-1K"
: "1K";
}
if (Math.abs(number) !== 0 && Math.abs(number) > 1000) {
const suffixes = ["", "K", "M", "B", "B", "T", "Q", "Qu", "S", "O", "N", "D"];
const magnitude = Math.floor(Math.log10(Math.abs(number)));
let index = Math.min(Math.floor(magnitude / 3), suffixes.length - 1);
// Special case to keep numbers in trillions formatted as billions
if (index >= 4) {
index = 3; // Keep the suffix at "B"
}
let abbreviation = Math.abs(number) / Math.pow(10, index * 3);
// Set the desired number of decimals
if (abbreviation >= 1000) {
abbreviation = abbreviation.toFixed(1);
index++;
} else {
abbreviation = abbreviation.toFixed(2);
}
// When stripTrailingZeros is true, we set the minimumFractionDigits to 0
const localeOptions = {
maximumFractionDigits: 2,
minimumFractionDigits: stripTrailingZeros ? 0 : 2,
};
abbreviation = parseFloat(abbreviation).toLocaleString("en-US", localeOptions);
const formattedNumber = abbreviation + suffixes[index];
return addDollarSign
? (negative ? "-$" : "$") + formattedNumber
: negative
? "-" + formattedNumber
: formattedNumber;
} else if (Math.abs(number) >= 0 && Math.abs(number) < 1000) {
return addDollarSign
? (negative ? "-$" : "$") + Math.abs(number)
: negative
? "-" + Math.abs(number)
: number.toString();
} else {
return addDollarSign ? "$0" : "0";
}
}
export function formatDate(dateStr) {
try {
// Parse the input date string in Berlin timezone
const berlinFormatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'Europe/Berlin',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
});
const parseDate = (date) => {
const parts = berlinFormatter?.formatToParts(date);
const extract = (type) =>
parts.find((p) => p.type === type)?.value.padStart(2, '0');
return new Date(
`${extract('year')}-${extract('month')}-${extract('day')}T${extract('hour')}:${extract('minute')}:${extract('second')}`
);
};
const berlinDateObj = parseDate(new Date(dateStr));
const berlinCurrentObj = parseDate(new Date());
// Calculate the time difference in seconds
const seconds = Math.floor((berlinCurrentObj - berlinDateObj) / 1000);
// Define time intervals including seconds so that even differences < 60 are handled
const intervals = [
{ unit: 'year', seconds: 31536000 },
{ unit: 'month', seconds: 2592000 },
{ unit: 'week', seconds: 604800 },
{ unit: 'day', seconds: 86400 },
{ unit: 'hour', seconds: 3600 },
{ unit: 'minute', seconds: 60 },
{ unit: 'second', seconds: 1 },
];
// Determine the appropriate time interval
for (const { unit, seconds: secondsInUnit } of intervals) {
const count = Math.floor(seconds / secondsInUnit);
if (count >= 1) {
return `${count} ${unit}${count === 1 ? '' : 's'} ago`;
}
}
// Fallback in case the difference is 0 seconds
return 'Just now';
} catch (error) {
console.error('Error formatting date:', error);
return 'Invalid date';
}
}
export const formatRuleValue = (rule) => {
if (["interestIncome", "interestExpenses"].includes(rule.name)) {
return `$${rule.value === 1000 ? `${rule.value / 1000} Bn` : `${rule.value} Mio`}`;
} else if (
[
"revenue",
"costOfRevenue",
"costAndExpenses",
"netIncome",
"grossProfit",
"researchAndDevelopmentExpenses",
"marketCap",
"operatingExpenses",
"operatingIncome",
"ebitda",
].includes(rule.name)
) {
return `${rule.condition} $${rule.value} Bn`;
} else if (rule.name === "aiSignal") {
return `${rule.value === "2" ? "Buy" : rule.value === "1" ? "Hold" : rule.value === "0" ? "Sell" : "n/a"} Signal`;
} else if (["avgVolume"].includes(rule.name)) {
return `${rule.condition} ${rule.value} Mio`;
} else if (["var"].includes(rule.name)) {
return `${rule.condition} ${rule.value}%`;
} else if (["ratingRecommendation"].includes(rule.name)) {
return rule.value === 2 ? "Buy" : rule.value === 1 ? "Hold" : "Sell";
} else if (["trendAnalysis", "fundamentalAnalysis"].includes(rule.name)) {
return `${rule.condition} ${rule.value}% Accuracy`;
} else {
return `${rule.condition} ${rule.value}${rule.name.includes("Growth") ? " %" : ""}`;
}
};
export function formatETFName(inputString) {
// Define special cases
const specialCases = {
"etf-mg": "ETFMG",
ark: "ARK",
"index-iq": "IndexIQ",
"bny-mellon": "BNY Mellon",
ssc: "SS&C",
pgim: "PGIM",
"jpmorgan-chase": "JPMorgan Chase",
"us-global-investors": "US Global Investors",
// Add more special cases as needed
};
// Check if the input string is a special case
const lowerCaseInput = inputString?.toLowerCase();
if (specialCases?.hasOwnProperty(lowerCaseInput)) {
return specialCases[lowerCaseInput];
}
// Split the input string into an array of words
const words = inputString?.split("-");
// Capitalize the first letter of each word
const capitalizedWords = words?.map(
(word) => word.charAt(0)?.toUpperCase() + word?.slice(1),
);
// Join the words back together with a space between them
const formattedString = capitalizedWords?.join(" ");
return formattedString;
}
// Function to add days to a given date
export function addDays(data, days, state) {
let result;
const createdDate = new Date(data?.user?.created);
result = new Date(createdDate);
result.setDate(result.getDate() + days);
if (state === "date") {
return result;
} else {
const differenceInTime = result - createdDate;
const differenceInDays = Math.round(
differenceInTime / (1000 * 60 * 60 * 24),
);
return Math.abs(differenceInDays);
}
}
export function pageTransitionIn(node, { duration, screenWidth }) {
if (screenWidth >= 640) {
return;
}
return {
duration,
css: (t) => {
const eased = cubicOut(t);
return `
transform: translateX(${(1 - eased) * 100}%);
width: 100%;
height: 100%;
opacity: 1;
z-index: 9999;
transition: transform ${duration}ms ease-out;
`;
},
};
}
export function pageTransitionOut(node, { duration }) {
return {
duration,
css: (t) => {
const eased = cubicOut(t);
return `
transform: translateX(${(1 - eased) * 100}%);
opacity: 1;
width: 100%;
height: 100%;
z-index: 9999;
transition: transform ${duration}ms ease-in;
`;
},
};
}
export function convertTimestamp(timestamp) {
const date = new Date(timestamp * 1000); // Convert seconds to milliseconds
return date.toLocaleString("en-US", {
year: "numeric", // e.g., "2024"
month: "short", // e.g., "Aug"
day: "2-digit", // e.g., "23"
hour: "numeric", // e.g., "3"
minute: "2-digit", // e.g., "59"
hour12: true, // e.g., "PM"
});
}
/*
function convertNYTimeToLocalTime(nyTimeString) {
// New York Time Zone
const nyTimeZone = 'America/New_York';
// Parse the New York time string
let nyTime = new Date(nyTimeString);
if (isNaN(nyTime)) {
throw new Error('Invalid date format');
}
// Convert New York time to UTC
let utcTime = new Date(nyTime.toLocaleString('en-US', { timeZone: nyTimeZone }));
// Create an Intl.DateTimeFormat object for local time zone
const localTimeFormatter = new Intl.DateTimeFormat('en-US', {
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
hour12: false, // Use 24-hour format
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
// Format the UTC time as local time
const localFormattedTime = localTimeFormatter.format(utcTime);
return localFormattedTime;
}
*/
export function getPartyForPoliticians(name) {
// Predefined list of senators and their parties
const senatorPartyMap = {
"Bryon Donalds": "Republican",
"Blake Moore": "Republican",
"Bill Hagerty": "Republican",
"Scott Peters": "Democratic",
"Jared Moskowitz": "Democratic",
"Suzan DelBene": "Democratic",
"Rudy Yakym": "Republican",
"Judy Chu": "Democratic",
"Michael Burgess": "Republican",
"David Perdue": "Republican",
"Pat Roberts": "Republican",
"Sheldon Whitehouse": "Democratic",
"Shelley Moore Capito": "Republican",
"Patty Murray": "Democratic",
"Susan Collins": "Republican",
"John Hoeven": "Republican",
"Tommy Tuberville": "Republican",
"Katie Britt": "Republican",
"Lisa Murkowski": "Republican",
"Dan Sullivan": "Republican",
"Kyrsten Sinema": "Independent",
"Mark Kelly": "Democratic",
"John Boozman": "Republican",
"Tom Cotton": "Republican",
"Alex Padilla": "Democratic",
"Laphonza Butler": "Democratic",
"Michael Bennet": "Democratic",
"John Hickenlooper": "Democratic",
"Richard Blumenthal": "Democratic",
"Chris Murphy": "Democratic",
"Tom Carper": "Democratic",
"Chris Coons": "Democratic",
"Marco Rubio": "Republican",
"Rick Scott": "Republican",
"Jon Ossoff": "Democratic",
"Raphael Warnock": "Democratic",
"Brian Schatz": "Democratic",
"Mazie Hirono": "Democratic",
"Mike Crapo": "Republican",
"Jim Risch": "Republican",
"Dick Durbin": "Democratic",
"Dianne Feinstein": "Democratic",
"Ben Ray Luján": "Democratic",
"Martin Heinrich": "Democratic",
"Catherine Cortez Masto": "Democratic",
"Jacky Rosen": "Democratic",
"Kevin Cramer": "Republican",
"Sherrod Brown": "Democratic",
"James Lankford": "Republican",
"Markwayne Mullin": "Republican",
"Jeff Merkley": "Democratic",
"Ron Wyden": "Democratic",
"Ron L Wyden": "Democratic",
"Bob Casey Jr.": "Democratic",
"Pat Toomey": "Republican",
"Jack Reed": "Democratic",
"Tim Scott": "Republican",
"Lindsey Graham": "Republican",
"Mike Rounds": "Republican",
"John Thune": "Republican",
"Marsha Blackburn": "Republican",
"Ted Cruz": "Republican",
"John Cornyn": "Republican",
"Mitt Romney": "Republican",
"Mike Lee": "Republican",
"Patrick Leahy": "Democratic",
"Bernie Sanders": "Independent",
"Mark Warner": "Democratic",
"Tim Kaine": "Democratic",
"Maria Cantwell": "Democratic",
"Joe Manchin": "Democratic",
"Ron Johnson": "Republican",
"Tammy Baldwin": "Democratic",
"John Barrasso": "Republican",
"Cynthia Lummis": "Republican",
"James Inhofe": "Republican",
"Kelly Loeffler": "Republican",
"Rick Larsen": "Democratic",
"Dwight Evans": "Democratic",
"Mark Green": "Republican",
"Kevin Hern": "Republican",
"Sean Casten": "Democratic",
"William Keating": "Democratic",
"Bill Keating": "Democratic",
"Max Miller": "Republican",
"Pete Sessions": "Republican",
"Jerry Moran": "Republican",
"Bill Cassidy": "Republican",
"Cory Booker": "Democratic",
"Deb Fischer": "Republican",
"John Ricketts": "Republican",
"Tammy Duckworth": "Democratic",
"Angus King": "Other",
"Gary Peters": "Democratic",
"Doris Matsui": "Democratic",
"Thomas Kean": "Republican",
"Debbie Wasserman Schultz": "Democratic",
"Josh Gottheimer": "Democratic",
"Lloyd Doggett": "Democratic",
"Michael Collins": "Democratic",
"Kathy Manning": "Democratic",
"Maria Elvira Salazar": "Republican",
"Jonathan Jackson": "Democratic",
"Mike Kelly": "Republican",
"Richard Allen": "Republican",
"James French Hill": "Republican",
"Virginia Foxx": "Republican",
"Chellie Pingree": "Democratic",
"Cliff Bentz": "Republican",
"Katherine Clark": "Democratic",
"Robert Latta": "Republican",
"Victoria Spartz": "Republican",
"Debbie Dingell": "Democratic",
"Garret Graves": "Republican",
"Shri Thanedar": "Democratic",
"Nancy Pelosi": "Democratic",
"Steve Cohen": "Democratic",
"Earl Blumenauer": "Democratic",
"Adrian Smith": "Republican",
"Michael Patrick Guest": "Republican",
"Michael Garcia": "Republican",
"Greg Steube": "Republican",
"Daniel Meuser": "Republican",
"Gerald Connolly": "Democratic",
"Brian Mast": "Republican",
"Nanette Barragan": "Democratic",
"Mark Pocan": "Democratic",
"Kathy Castor": "Democratic",
"Donald Sternoff Beyer": "Democratic",
"Thomas Suozzi": "Democratic",
"Eleanor Holmes Norton": "Democratic",
"Chip Roy": "Republican",
"Tracey Robert Mann": "Republican",
"Felix Barry Moore": "Republican",
"Dan Newhouse": "Republican",
"Mike Garcia": "Republican",
"Scott Franklin": "Republican",
"Michael McCaul": "Republican",
"Ro Khanna": "Democratic",
"Daniel Goldman": "Democratic",
"Greg Stanton": "Democratic",
"Chris Jacobs": "Republican",
"Robert Aderholt": "Republican",
"David McKinley": "Republican",
"Kim Schrier": "Democratic",
"Vicente Gonzalez": "Democratic",
"Dan Crenshaw": "Republican",
"Marie Newman": "Democratic",
"Dean Phillips": "Democratic",
"Roger Marshall": "Republican",
"Zoe Lofgren": "Democratic",
"John Curtis": "Republican",
"Don Beyer": "Democratic",
"Ed Perlmutter": "Democratic",
"James Langevin": "Democratic",
"Kenny Marchant": "Republican",
"David Kustoff": "Republican",
"Marjorie Taylor Greene": "Republican",
"Deborah Ross": "Democratic",
"Peter Meijer": "Republican",
"Donna Shalala": "Democratic",
"Brenda Lulenar Lawrence": "Democratic",
"Robert Wittman": "Republican",
"Diana Harshbarger": "Republican",
"Morgan McGarvey": "Democratic",
"Eric Burlison": "Republican",
"Cynthia Axne": "Democratic",
"Seth Moulton": "Democratic",
"Anthony Gonzalez": "Republican",
"David Joyce": "Republican",
"Alan Lowenthal": "Democratic",
"Patrick Fallon": "Republican",
"David Madison Cawthorn": "Republican",
"Ashley Hinson Arenholz": "Republican",
"Gilbert Cisneros": "Democratic",
"Joseph Morelle": "Democratic",
"Joe Courtney": "Democratic",
"Michael Conaway": "Republican",
"William Timmons": "Republican",
"Cheri Bustos": "Democratic",
"Harley Rouda": "Democratic",
"Susan Brooks": "Republican",
"Mikie Sherrill": "Democratic",
"David Cheston Rouzer": "Republican",
"Bradley Schneider": "Democratic",
"Justin Amash": "Other",
"Lamar Smith": "Republican",
"Gary Palmer": "Republican",
"Barbara Comstock": "Republican",
"Thomas Rooney": "Republican",
"Carlos Curbelo": "Republican",
"Bob Gibbs": "Republican",
"Kurt Schrader": "Democratic",
"August Lee Pfluger": "Republican",
"Ed Case": "Democratic",
"Francis Rooney": "Republican",
"John Rutherford": "Republican",
"David Roe": "Republican",
"Sean Patrick Maloney": "Democratic",
"Peter Welch": "Democratic",
"Lois Frankel": "Democratic",
"Carol Devine Miller": "Republican",
"Mo Brooks": "Republican",
"Bobby Scott": "Democratic",
"John Yarmuth": "Democratic",
"Mike Gallagher": "Republican",
"Tom O'Halleran": "Democratic",
"David Price": "Democratic",
"Tina Smith": "Democratic",
"Doug Lamborn": "Republican",
"Gerry Connolly": "Democratic",
"Chuck Fleischmann": "Republican",
"James Vance": "Republican",
"Neal Dunn": "Republican",
"Anna Paulina Luna": "Republican",
"Laurel Lee": "Republican",
"Mitch McConnell": "Republican",
"Hal Rogers": "Republican",
"Jennifer McClellan": "Democratic",
"Patrick McHenry": "Republican",
"Susie Lee": "Democratic",
"Jim Banks": "Republican",
"Michael San Nicolas": "Democratic",
"Mary Gay Scanlon": "Democratic",
"Van Taylor": "Republican",
"Ron Estes": "Republican",
"Chris Van Hollen": "Democratic",
"Stephen Lynch": "Democratic",
"Ann Wagner": "Republican",
"Michael Simpson": "Republican",
"Thom Tillis": "Republican",
"Glenn Grothman": "Republican",
"Tom Cole": "Republican",
"David Trone": "Democratic",
"Fred Upton": "Republican",
"John Larson": "Democratic",
"Tom Malinowski": "Democratic",
"Jeff Duncan": "Republican",
"Peter Visclosky": "Democratic",
"Adam Kinzinger": "Republican",
"Austin Scott": "Republican",
"Abigail Spanberger": "Democratic",
"Roger Williams": "Republican",
"Earl Leroy Carter": "Republican",
"Daniel Webster": "Republican",
"Nicole Malliotakis": "Republican",
"Buddy Carter": "Republican",
"John Fetterman": "Democratic",
"Sharice Davids": "Democratic",
"Jake Auchincloss": "Democratic",
"John James": "Republican",
"Greg Landsman": "Democratic",
"Darin LaHood": "Republican",
"Darrell Issa": "Republican",
"Lance Gooden": "Republican",
"Byron Donalds": "Republican",
"James Comer": "Republican",
"Guy Reschenthaler": "Republican",
"Keith Self": "Republican",
"Gus Bilirakis": "Republican",
"Dave McCormick": "Republican",
"Gil Cisneros": "Democratic",
"Emily Randall": "Democratic",
"Julie Johnson": "Democratic",
"Rob Bresnahan": "Republican",
"Tim Moore": "Republican",
"Jefferson Shreve": "Republican",
"Ashley Moody": "Republican",
"Brad Knott": "Republican",
};
// Combine first and last name to form the key
const nameKey = `${name}`?.trim();
// Return the party, or 'Unknown' if not found
return senatorPartyMap[nameKey] || "Other";
}
export const listOfCountries = [
"Afghanistan",
"Albania",
"Algeria",
"Andorra",
"Angola",
"Antigua and Barbuda",
"Argentina",
"Armenia",
"Australia",
"Austria",
"Azerbaijan",
"Bahamas",
"Bahrain",
"Bangladesh",
"Barbados",
"Belarus",
"Belgium",
"Belize",
"Benin",
"Bhutan",
"Bolivia",
"Bosnia and Herzegovina",
"Botswana",
"Brazil",
"Brunei",
"Bulgaria",
"Burkina Faso",
"Burundi",
"Cabo Verde",
"Cambodia",
"Cameroon",
"Canada",
"Central African Republic",
"Chad",
"Chile",
"China",
"Colombia",
"Comoros",
"Congo (Congo-Brazzaville)",
"Costa Rica",
"Croatia",
"Cuba",
"Cyprus",
"Czechia (Czech Republic)",
"Democratic Republic of the Congo",
"Denmark",
"Djibouti",
"Dominica",
"Dominican Republic",
"Ecuador",
"Egypt",
"El Salvador",
"Equatorial Guinea",
"Eritrea",
"Estonia",
"Eswatini (fmr. 'Swaziland')",
"Ethiopia",
"Fiji",
"Finland",
"France",
"Gabon",
"Gambia",
"Georgia",
"Germany",
"Ghana",
"Greece",
"Grenada",
"Guatemala",
"Guinea",
"Guinea-Bissau",
"Guyana",
"Haiti",
"Holy See",
"Honduras",
"Hong Kong",
"Hungary",
"Iceland",
"India",
"Indonesia",
"Iran",
"Iraq",
"Ireland",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Jordan",
"Kazakhstan",
"Kenya",
"Kiribati",
"Kuwait",
"Kyrgyzstan",
"Laos",
"Latvia",
"Lebanon",
"Lesotho",
"Liberia",
"Libya",
"Liechtenstein",
"Lithuania",
"Luxembourg",
"Madagascar",
"Malawi",
"Malaysia",
"Maldives",
"Mali",
"Malta",
"Marshall Islands",
"Mauritania",
"Mauritius",
"Mexico",
"Micronesia",
"Moldova",
"Monaco",
"Mongolia",
"Montenegro",
"Morocco",
"Mozambique",
"Myanmar (formerly Burma)",
"Namibia",
"Nauru",
"Nepal",
"Netherlands",
"New Zealand",
"Nicaragua",
"Niger",
"Nigeria",
"North Korea",
"North Macedonia",
"Norway",
"Oman",
"Pakistan",
"Palau",
"Palestine",
"Panama",
"Papua New Guinea",
"Paraguay",
"Peru",
"Philippines",
"Poland",
"Portugal",
"Qatar",
"Romania",
"Russia",
"Rwanda",
"Saint Kitts and Nevis",
"Saint Lucia",
"Saint Vincent and the Grenadines",
"Samoa",
"San Marino",
"Sao Tome and Principe",
"Saudi Arabia",
"Senegal",
"Serbia",
"Seychelles",
"Sierra Leone",
"Singapore",
"Slovakia",
"Slovenia",
"Solomon Islands",
"Somalia",
"South Africa",
"South Korea",
"South Sudan",
"Spain",
"Sri Lanka",
"Sudan",
"Suriname",
"Sweden",
"Switzerland",
"Syria",
"Tajikistan",
"Tanzania",
"Taiwan",
"Thailand",
"Timor-Leste",
"Togo",
"Tonga",
"Trinidad and Tobago",
"Tunisia",
"Turkey",
"Turkmenistan",
"Tuvalu",
"Uganda",
"Ukraine",
"United Arab Emirates",
"UK",
"United States",
"Uruguay",
"Uzbekistan",
"Vanuatu",
"Venezuela",
"Vietnam",
"Yemen",
"Zambia",
"Zimbabwe",
];
export const listOfRelevantCountries = [
"United States", // By far the most companies listed on the NYSE
"China",
"Canada",
"United Kingdom",
"Japan",
"Israel",
"Brazil",
"France",
"Ireland",
"Germany",
"Mexico",
"India",
"Australia",
"South Korea",
"Sweden",
"Netherlands",
"Switzerland",
"Taiwan",
"South Africa",
"Hong Kong",
"Singapore",
"Argentina",
"Chile",
"Philippines",
"Turkey",
"Italy",
"Indonesia",
"Malaysia",
"Luxembourg",
"Vietnam",
"New Zealand",
"Denmark",
"Norway",
"Finland",
"Russia",
"United Arab Emirates",
];
export const sectorList = [
"Basic Materials",
"Communication Services",
"Consumer Cyclical",
"Consumer Defensive",
"Energy",
"Financial Services",
"Healthcare",
"Industrials",
"Real Estate",
"Technology",
"Utilities",
];
export const industryList = [
"Steel",
"Silver",
"Other Precious Metals",
"Gold",
"Copper",
"Aluminum",
"Paper, Lumber & Forest Products",
"Industrial Materials",
"Construction Materials",
"Chemicals - Specialty",
"Chemicals",
"Agricultural Inputs",
"Telecommunications Services",
"Internet Content & Information",
"Publishing",
"Broadcasting",
"Advertising Agencies",
"Entertainment",
"Travel Lodging",
"Travel Services",
"Specialty Retail",
"Luxury Goods",
"Home Improvement",
"Residential Construction",
"Department Stores",
"Personal Products & Services",
"Leisure",
"Gambling, Resorts & Casinos",
"Furnishings, Fixtures & Appliances",
"Restaurants",
"Auto - Parts",
"Auto - Manufacturers",
"Auto - Recreational Vehicles",
"Auto - Dealerships",
"Apparel - Retail",
"Apparel - Manufacturers",
"Apparel - Footwear & Accessories",
"Packaging & Containers",
"Tobacco",
"Grocery Stores",
"Discount Stores",
"Household & Personal Products",
"Packaged Foods",
"Food Distribution",
"Food Confectioners",
"Agricultural Farm Products",
"Education & Training Services",
"Beverages - Wineries & Distilleries",
"Beverages - Non-Alcoholic",
"Beverages - Alcoholic",
"Uranium",
"Solar",
"Oil & Gas Refining & Marketing",
"Oil & Gas Midstream",
"Oil & Gas Integrated",
"Oil & Gas Exploration & Production",
"Oil & Gas Equipment & Services",
"Oil & Gas Energy",
"Oil & Gas Drilling",
"Coal",
"Shell Companies",
"Investment - Banking & Investment Services",
"Insurance - Specialty",
"Insurance - Reinsurance",
"Insurance - Property & Casualty",
"Insurance - Life",
"Insurance - Diversified",
"Insurance - Brokers",
"Financial - Mortgages",
"Financial - Diversified",
"Financial - Data & Stock Exchanges",
"Financial - Credit Services",
"Financial - Conglomerates",
"Financial - Capital Markets",
"Banks - Regional",
"Banks - Diversified",
"Banks",
"Asset Management",
"Asset Management - Bonds",
"Asset Management - Income",
"Asset Management - Leveraged",
"Asset Management - Cryptocurrency",
"Asset Management - Global",
"Medical - Specialties",
"Medical - Pharmaceuticals",
"Medical - Instruments & Supplies",
"Medical - Healthcare Plans",
"Medical - Healthcare Information Services",
"Medical - Equipment & Services",
"Medical - Distribution",
"Medical - Diagnostics & Research",
"Medical - Devices",
"Medical - Care Facilities",
"Drug Manufacturers - Specialty & Generic",
"Drug Manufacturers - General",
"Biotechnology",
"Waste Management",
"Trucking",
"Railroads",
"Aerospace & Defense",
"Marine Shipping",
"Integrated Freight & Logistics",
"Airlines, Airports & Air Services",
"General Transportation",
"Manufacturing - Tools & Accessories",
"Manufacturing - Textiles",
"Manufacturing - Miscellaneous",
"Manufacturing - Metal Fabrication",
"Industrial - Distribution",
"Industrial - Specialties",
"Industrial - Pollution & Treatment Controls",
"Environmental Services",
"Industrial - Machinery",
"Industrial - Infrastructure Operations",
"Industrial - Capital Goods",
"Consulting Services",
"Business Equipment & Supplies",
"Staffing & Employment Services",
"Rental & Leasing Services",
"Engineering & Construction",
"Security & Protection Services",
"Specialty Business Services",
"Construction",
"Conglomerates",
"Electrical Equipment & Parts",
"Agricultural - Machinery",
"Agricultural - Commodities/Milling",
"REIT - Specialty",
"REIT - Retail",
"REIT - Residential",
"REIT - Office",
"REIT - Mortgage",
"REIT - Industrial",
"REIT - Hotel & Motel",
"REIT - Healthcare Facilities",
"REIT - Diversified",
"Real Estate - Services",
"Real Estate - Diversified",
"Real Estate - Development",
"Real Estate - General",
"Information Technology Services",
"Hardware, Equipment & Parts",
"Computer Hardware",
"Electronic Gaming & Multimedia",
"Software - Services",
"Software - Infrastructure",
"Software - Application",
"Semiconductors",
"Media & Entertainment",
"Communication Equipment",
"Technology Distributors",
"Consumer Electronics",
"Renewable Utilities",
"Regulated Water",
"Regulated Gas",
"Regulated Electric",
"Independent Power Producers",
"Diversified Utilities",
"General Utilities",
];
export const monthNames = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];
export const holidays = ['2025-01-01', '2025-01-09','2025-01-20', '2025-02-17', '2025-04-18', '2025-05-26', '2025-06-19', '2025-07-04', '2025-09-01', '2025-11-27', '2025-12-25']
export const getLastTradingDay = () => {
const etTimeZone = "America/New_York";
// Helper function to check if a date (in NY time) is a holiday
const isHoliday = (date) => {
return holidays.includes(date.toISOString().split("T")[0]);
};
let date = new Date();
// Convert current date to NY timezone
const nyDate = new Date(
date.toLocaleString("en-US", { timeZone: etTimeZone }),
);
// Loop backwards to find the most recent trading day
while (true) {
const dayOfWeek = nyDate.getUTCDay();
// Check if it's a weekday and not a holiday
if (dayOfWeek !== 0 && dayOfWeek !== 6 && !isHoliday(nyDate)) {
return nyDate.toISOString().split("T")[0];
}
// Move back one day
nyDate.setDate(nyDate.getDate() - 1);
}
};
export const sectorNavigation = [
{
title: "Financial Services",
link: "/list/sector/financial",
},
{
title: "Finance",
link: "/list/sector/financial",
},
{
title: "Healthcare",
link: "/list/sector/healthcare",
},
{
title: "Technology",
link: "/list/sector/technology",
},
{
title: "Industrials",
link: "/list/sector/industrials",
},
{
title: "Energy",
link: "/list/sector/energy",
},
{
title: "Utilities",
link: "/list/sector/utilities",
},
{
title: "Consumer Cyclical",
link: "/list/sector/consumer-cyclical",
},
{
title: "Real Estate",
link: "/list/sector/real-estate",
},
{
title: "Basic Materials",
link: "/list/sector/basic-materials",
},
{
title: "Communication Services",
link: "/list/sector/communication-services",
},
{
title: "Consumer Defensive",
link: "/list/sector/consumer-defensive",
},
];