@fin.cx/opendata
Version:
A comprehensive TypeScript library for accessing business data and real-time financial information. Features include German company data management with MongoDB integration, JSONL bulk processing, automated Handelsregister interactions, and real-time stoc
602 lines • 49.4 kB
JavaScript
import * as plugins from '../../plugins.js';
/**
* Marketstack API v2 Provider - Professional Plan with Intelligent Intraday Support
* Documentation: https://docs.apilayer.com/marketstack/docs/marketstack-api-v2-v-2-0-0
*
* Features:
* - Intelligent endpoint selection based on market hours
* - Real-time intraday pricing with multiple intervals (1min, 5min, 10min, 15min, 30min, 1hour)
* - End-of-Day (EOD) stock prices with historical data
* - Market state detection (PRE, REGULAR, POST, CLOSED)
* - Exchange filtering via MIC codes (XNAS, XNYS, XLON, etc.)
* - Supports 500,000+ tickers across 72+ exchanges worldwide
* - OHLCV data (Open, High, Low, Close, Volume)
* - Pagination for large datasets
* - Automatic fallback from intraday to EOD on errors
* - Requires API key authentication
*
* Intelligent Endpoint Selection:
* - During market hours (PRE/REGULAR/POST): Uses intraday endpoints for fresh data
* - After market close (CLOSED): Uses EOD endpoints to save API credits
* - Automatic fallback to EOD if intraday fails (rate limits, plan restrictions, etc.)
*
* Rate Limits:
* - Free Plan: 100 requests/month (EOD only)
* - Basic Plan: 10,000 requests/month (EOD only)
* - Professional Plan: 100,000 requests/month (intraday + EOD)
*
* Intraday Access:
* - Intervals below 15min (1min, 5min, 10min) require Professional Plan or higher
* - Real-time data from IEX Exchange for US tickers
* - Symbol formatting: Periods replaced with hyphens for intraday (BRK.B → BRK-B)
*/
export class MarketstackProvider {
config;
name = 'Marketstack';
priority = 90; // Increased from 80 - now supports real-time intraday data during market hours
requiresAuth = true;
rateLimit = {
requestsPerMinute: 0, // No per-minute limit specified
requestsPerDay: undefined // Varies by plan
};
logger = console;
baseUrl = 'https://api.marketstack.com/v2';
apiKey;
constructor(apiKey, config) {
this.config = config;
if (!apiKey) {
throw new Error('API key is required for Marketstack provider');
}
this.apiKey = apiKey;
}
/**
* Unified data fetching method supporting all request types
*/
async fetchData(request) {
switch (request.type) {
case 'current':
return this.fetchCurrentPrice(request);
case 'historical':
return this.fetchHistoricalPrices(request);
case 'intraday':
return this.fetchIntradayPrices(request);
case 'batch':
return this.fetchBatchCurrentPrices(request);
default:
throw new Error(`Unsupported request type: ${request.type}`);
}
}
/**
* Fetch current/latest price with intelligent endpoint selection
* Uses intraday during market hours (PRE, REGULAR, POST) for fresh data
* Uses EOD after market close (CLOSED) to save API credits
*/
async fetchCurrentPrice(request) {
try {
// Determine current market state
const marketState = this.getUsMarketState();
const useIntraday = this.shouldUseIntradayEndpoint(marketState);
if (useIntraday) {
// Use intraday endpoint for fresh data during market hours
return await this.fetchCurrentPriceIntraday(request, marketState);
}
else {
// Use EOD endpoint for after-close data
return await this.fetchCurrentPriceEod(request);
}
}
catch (error) {
// If intraday fails, fallback to EOD with warning
const errorMessage = plugins.getErrorMessage(error);
if (errorMessage.includes('intraday') || errorMessage.includes('Marketstack API error')) {
this.logger.warn(`Intraday endpoint failed for ${request.ticker}, falling back to EOD:`, errorMessage);
try {
return await this.fetchCurrentPriceEod(request);
}
catch (eodError) {
// Both failed, throw original error
throw error;
}
}
throw error;
}
}
/**
* Fetch current price using intraday endpoint (during market hours)
* Uses 1min interval for most recent data
*/
async fetchCurrentPriceIntraday(request, marketState) {
const formattedSymbol = this.formatSymbolForIntraday(request.ticker);
let url = `${this.baseUrl}/tickers/${formattedSymbol}/intraday/latest?access_key=${this.apiKey}`;
url += `&interval=1min`; // Use 1min for most recent data
// Add exchange filter if specified
if (request.exchange) {
url += `&exchange=${request.exchange}`;
}
const response = await plugins.smartrequest.SmartRequest.create()
.url(url)
.timeout(this.config?.timeout || 10000)
.get();
const responseData = await response.json();
// Check for API errors
if (responseData.error) {
throw new Error(`Marketstack API error: ${responseData.error.message || JSON.stringify(responseData.error)}`);
}
if (!responseData || !responseData.close) {
throw new Error(`No intraday data found for ticker ${request.ticker}`);
}
return this.mapToStockPrice(responseData, 'intraday', marketState);
}
/**
* Fetch current price using EOD endpoint (after market close)
*/
async fetchCurrentPriceEod(request) {
let url = `${this.baseUrl}/tickers/${request.ticker}/eod/latest?access_key=${this.apiKey}`;
// Add exchange filter if specified
if (request.exchange) {
url += `&exchange=${request.exchange}`;
}
const response = await plugins.smartrequest.SmartRequest.create()
.url(url)
.timeout(this.config?.timeout || 10000)
.get();
const responseData = await response.json();
// Check for API errors
if (responseData.error) {
throw new Error(`Marketstack API error: ${responseData.error.message || JSON.stringify(responseData.error)}`);
}
// For single ticker endpoint, response is direct object (not wrapped in data field)
if (!responseData || !responseData.close) {
throw new Error(`No data found for ticker ${request.ticker}`);
}
return this.mapToStockPrice(responseData, 'eod');
}
/**
* Fetch historical EOD prices for a ticker with date range
*/
async fetchHistoricalPrices(request) {
try {
const allPrices = [];
let offset = request.offset || 0;
const limit = request.limit || 1000; // Max per page
const maxRecords = this.config?.maxRecords || 10000; // Safety limit (configurable)
while (true) {
let url = `${this.baseUrl}/eod?access_key=${this.apiKey}`;
url += `&symbols=${request.ticker}`;
url += `&date_from=${this.formatDate(request.from)}`;
url += `&date_to=${this.formatDate(request.to)}`;
url += `&limit=${limit}`;
url += `&offset=${offset}`;
if (request.exchange) {
url += `&exchange=${request.exchange}`;
}
if (request.sort) {
url += `&sort=${request.sort}`;
}
const response = await plugins.smartrequest.SmartRequest.create()
.url(url)
.timeout(this.config?.timeout || 15000)
.get();
const responseData = await response.json();
// Check for API errors
if (responseData.error) {
throw new Error(`Marketstack API error: ${responseData.error.message || JSON.stringify(responseData.error)}`);
}
if (!responseData?.data || !Array.isArray(responseData.data)) {
throw new Error('Invalid response format from Marketstack API');
}
// Map data to stock prices
for (const data of responseData.data) {
try {
allPrices.push(this.mapToStockPrice(data, 'eod'));
}
catch (error) {
this.logger.warn(`Failed to parse historical data for ${data.symbol}:`, error);
}
}
// Check if we have more pages
const pagination = responseData.pagination;
const hasMore = pagination && offset + limit < pagination.total;
// Safety check: don't fetch more than maxRecords
if (!hasMore || allPrices.length >= maxRecords) {
break;
}
offset += limit;
}
return allPrices;
}
catch (error) {
this.logger.error(`Failed to fetch historical prices for ${request.ticker}:`, error);
throw new Error(`Marketstack: Failed to fetch historical prices for ${request.ticker}: ${plugins.getErrorMessage(error)}`);
}
}
/**
* Fetch intraday prices with specified interval
* Supports intervals: 1min, 5min, 10min, 15min, 30min, 1hour
* Note: Intervals below 15min require Professional Plan or higher
*/
async fetchIntradayPrices(request) {
try {
const allPrices = [];
let offset = 0;
const limit = 1000; // Max per page for intraday
const maxRecords = this.config?.maxRecords || 10000; // Safety limit (configurable)
// Apply default limit if user didn't specify one (performance optimization)
const effectiveLimit = request.limit || this.config?.defaultIntradayLimit || 1000;
// Warn if fetching large amount of data without explicit limit
if (!request.limit && effectiveLimit > 1000) {
this.logger.warn(`Intraday request for ${request.ticker} without explicit limit will fetch up to ${effectiveLimit} records. ` +
`Consider adding a limit to the request for better performance.`);
}
// Format symbol for intraday endpoint (replace . with -)
const formattedSymbol = this.formatSymbolForIntraday(request.ticker);
while (true) {
let url = `${this.baseUrl}/tickers/${formattedSymbol}/intraday?access_key=${this.apiKey}`;
url += `&interval=${request.interval}`;
url += `&limit=${limit}`;
url += `&offset=${offset}`;
// Add date filter if specified
if (request.date) {
url += `&date_from=${this.formatDate(request.date)}`;
url += `&date_to=${this.formatDate(request.date)}`;
}
// Add exchange filter if specified
if (request.exchange) {
url += `&exchange=${request.exchange}`;
}
const response = await plugins.smartrequest.SmartRequest.create()
.url(url)
.timeout(this.config?.timeout || 15000)
.get();
const responseData = await response.json();
// Check for API errors
if (responseData.error) {
throw new Error(`Marketstack API error: ${responseData.error.message || JSON.stringify(responseData.error)}`);
}
if (!responseData?.data || !Array.isArray(responseData.data)) {
throw new Error('Invalid response format from Marketstack API');
}
// Map data to stock prices
for (const data of responseData.data) {
try {
allPrices.push(this.mapToStockPrice(data, 'intraday'));
}
catch (error) {
this.logger.warn(`Failed to parse intraday data for ${data.symbol}:`, error);
}
}
// Check if we have more pages
const pagination = responseData.pagination;
const hasMore = pagination && offset + limit < pagination.total;
// Honor effective limit or safety maxRecords
if (!hasMore || allPrices.length >= effectiveLimit || allPrices.length >= maxRecords) {
break;
}
offset += limit;
}
// Apply effective limit
if (allPrices.length > effectiveLimit) {
return allPrices.slice(0, effectiveLimit);
}
return allPrices;
}
catch (error) {
this.logger.error(`Failed to fetch intraday prices for ${request.ticker}:`, error);
throw new Error(`Marketstack: Failed to fetch intraday prices for ${request.ticker}: ${plugins.getErrorMessage(error)}`);
}
}
/**
* Fetch current prices for multiple tickers with intelligent endpoint selection
* Uses intraday during market hours (PRE, REGULAR, POST) for fresh data
* Uses EOD after market close (CLOSED) to save API credits
*/
async fetchBatchCurrentPrices(request) {
try {
// Determine current market state
const marketState = this.getUsMarketState();
const useIntraday = this.shouldUseIntradayEndpoint(marketState);
if (useIntraday) {
return await this.fetchBatchCurrentPricesIntraday(request, marketState);
}
else {
return await this.fetchBatchCurrentPricesEod(request);
}
}
catch (error) {
// Fallback to EOD if intraday fails
const errorMessage = plugins.getErrorMessage(error);
if (errorMessage.includes('intraday') || errorMessage.includes('Marketstack API error')) {
this.logger.warn(`Intraday batch endpoint failed, falling back to EOD:`, errorMessage);
try {
return await this.fetchBatchCurrentPricesEod(request);
}
catch (eodError) {
// Both failed, throw original error
throw error;
}
}
throw error;
}
}
/**
* Fetch batch current prices using intraday endpoint
*/
async fetchBatchCurrentPricesIntraday(request, marketState) {
// Format symbols for intraday (replace . with -)
const formattedSymbols = request.tickers.map(t => this.formatSymbolForIntraday(t)).join(',');
let url = `${this.baseUrl}/intraday/latest?access_key=${this.apiKey}`;
url += `&symbols=${formattedSymbols}`;
url += `&interval=1min`; // Use 1min for most recent data
if (request.exchange) {
url += `&exchange=${request.exchange}`;
}
const response = await plugins.smartrequest.SmartRequest.create()
.url(url)
.timeout(this.config?.timeout || 15000)
.get();
const responseData = await response.json();
if (responseData.error) {
throw new Error(`Marketstack API error: ${responseData.error.message || JSON.stringify(responseData.error)}`);
}
if (!responseData?.data || !Array.isArray(responseData.data)) {
throw new Error('Invalid response format from Marketstack API');
}
const prices = [];
for (const data of responseData.data) {
try {
prices.push(this.mapToStockPrice(data, 'intraday', marketState));
}
catch (error) {
this.logger.warn(`Failed to parse intraday data for ${data.symbol}:`, error);
}
}
if (prices.length === 0) {
throw new Error('No valid price data received from batch intraday request');
}
return prices;
}
/**
* Fetch batch current prices using EOD endpoint
*/
async fetchBatchCurrentPricesEod(request) {
const symbols = request.tickers.join(',');
let url = `${this.baseUrl}/eod/latest?access_key=${this.apiKey}&symbols=${symbols}`;
if (request.exchange) {
url += `&exchange=${request.exchange}`;
}
const response = await plugins.smartrequest.SmartRequest.create()
.url(url)
.timeout(this.config?.timeout || 15000)
.get();
const responseData = await response.json();
// Check for API errors
if (responseData.error) {
throw new Error(`Marketstack API error: ${responseData.error.message || JSON.stringify(responseData.error)}`);
}
if (!responseData?.data || !Array.isArray(responseData.data)) {
throw new Error('Invalid response format from Marketstack API');
}
const prices = [];
for (const data of responseData.data) {
try {
prices.push(this.mapToStockPrice(data, 'eod'));
}
catch (error) {
this.logger.warn(`Failed to parse data for ${data.symbol}:`, error);
// Continue processing other tickers
}
}
if (prices.length === 0) {
throw new Error('No valid price data received from batch request');
}
return prices;
}
/**
* Check if the Marketstack API is available and accessible
*/
async isAvailable() {
try {
// Test with a well-known ticker
const url = `${this.baseUrl}/tickers/AAPL/eod/latest?access_key=${this.apiKey}`;
const response = await plugins.smartrequest.SmartRequest.create()
.url(url)
.timeout(5000)
.get();
const responseData = await response.json();
// Check if we got valid data (not an error)
// Single ticker endpoint returns direct object, not wrapped in data field
return !responseData.error && responseData.close !== undefined;
}
catch (error) {
this.logger.warn('Marketstack provider is not available:', error);
return false;
}
}
/**
* Check if a market is supported
* Marketstack supports 72+ exchanges worldwide
*/
supportsMarket(market) {
// Marketstack has broad international coverage including:
// US, UK, DE, FR, JP, CN, HK, AU, CA, IN, etc.
const supportedMarkets = [
'US', 'UK', 'GB', 'DE', 'FR', 'JP', 'CN', 'HK', 'AU', 'CA',
'IN', 'BR', 'MX', 'IT', 'ES', 'NL', 'SE', 'CH', 'NO', 'DK'
];
return supportedMarkets.includes(market.toUpperCase());
}
/**
* Check if a ticker format is supported
*/
supportsTicker(ticker) {
// Basic validation - Marketstack supports most standard ticker formats
return /^[A-Z0-9\.\-]{1,10}$/.test(ticker.toUpperCase());
}
/**
* Map Marketstack API response to IStockPrice interface
* @param data - API response data
* @param dataType - Type of data (eod, intraday, live)
* @param explicitMarketState - Override market state (used for intraday data fetched during known market hours)
*/
mapToStockPrice(data, dataType = 'eod', explicitMarketState) {
if (!data.close) {
throw new Error('Missing required price data');
}
// Calculate change and change percent
// EOD data: previous close is typically open price of the same day
// For better accuracy, we'd need previous day's close, but that requires another API call
const currentPrice = data.close;
const previousClose = data.open || currentPrice;
const change = currentPrice - previousClose;
const changePercent = previousClose !== 0 ? (change / previousClose) * 100 : 0;
// Parse timestamp
const timestamp = data.date ? new Date(data.date) : new Date();
const fetchedAt = new Date();
// Determine market state intelligently
let marketState;
if (explicitMarketState) {
// Use provided market state (for intraday data fetched during known market hours)
marketState = explicitMarketState;
}
else if (dataType === 'eod') {
// EOD data is always for closed markets
marketState = 'CLOSED';
}
else if (dataType === 'intraday') {
// For intraday data without explicit state, determine from timestamp
marketState = this.getUsMarketState(timestamp.getTime());
}
else {
// Default fallback
marketState = 'CLOSED';
}
const stockPrice = {
ticker: data.symbol.toUpperCase(),
price: currentPrice,
currency: data.price_currency || 'USD',
change: change,
changePercent: changePercent,
previousClose: previousClose,
timestamp: timestamp,
provider: this.name,
marketState: marketState, // Now dynamic based on data type, timestamp, and explicit state
exchange: data.exchange,
exchangeName: data.exchange_code || data.name,
// Phase 1 enhancements: OHLCV data
volume: data.volume,
open: data.open,
high: data.high,
low: data.low,
adjusted: data.adj_close !== undefined, // If adj_close exists, price is adjusted
dataType: dataType,
fetchedAt: fetchedAt,
// Company identification
companyName: data.company_name || data.name || undefined,
companyFullName: this.buildCompanyFullName(data)
};
return stockPrice;
}
/**
* Build full company name with exchange and ticker information
* Example: "Apple Inc (NASDAQ:AAPL)"
*/
buildCompanyFullName(data) {
// Check if API already provides full name
if (data.full_name || data.long_name) {
return data.full_name || data.long_name;
}
// Build from available data
const companyName = data.company_name || data.name;
const exchangeCode = data.exchange_code; // e.g., "NASDAQ"
const symbol = data.symbol; // e.g., "AAPL"
if (!companyName) {
return undefined;
}
// If we have exchange and symbol, build full name: "Apple Inc (NASDAQ:AAPL)"
if (exchangeCode && symbol) {
return `${companyName} (${exchangeCode}:${symbol})`;
}
// If we only have symbol: "Apple Inc (AAPL)"
if (symbol) {
return `${companyName} (${symbol})`;
}
// Otherwise just return company name
return companyName;
}
/**
* Format date to YYYY-MM-DD for API requests
*/
formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
/**
* Get US market state based on Eastern Time
* Regular hours: 9:30 AM - 4:00 PM ET
* Pre-market: 4:00 AM - 9:30 AM ET
* After-hours: 4:00 PM - 8:00 PM ET
*
* @param timestampMs - Optional timestamp in milliseconds (defaults to current time)
* @returns Market state: PRE, REGULAR, POST, or CLOSED
*/
getUsMarketState(timestampMs) {
const now = timestampMs ? new Date(timestampMs) : new Date();
// Convert to ET (UTC-5 or UTC-4 depending on DST)
// For simplicity, we'll use a rough approximation
// TODO: Add proper timezone library for production use
const etOffset = -5; // Standard time, adjust for DST if needed
const etTime = new Date(now.getTime() + (etOffset * 60 * 60 * 1000));
// Get day of week (0 = Sunday, 6 = Saturday)
const dayOfWeek = etTime.getUTCDay();
// Check if weekend
if (dayOfWeek === 0 || dayOfWeek === 6) {
return 'CLOSED';
}
// Get hour and minute in ET
const hours = etTime.getUTCHours();
const minutes = etTime.getUTCMinutes();
const timeInMinutes = hours * 60 + minutes;
// Define market hours in minutes
const preMarketStart = 4 * 60; // 4:00 AM
const regularMarketStart = 9 * 60 + 30; // 9:30 AM
const regularMarketEnd = 16 * 60; // 4:00 PM
const afterHoursEnd = 20 * 60; // 8:00 PM
if (timeInMinutes >= preMarketStart && timeInMinutes < regularMarketStart) {
return 'PRE';
}
else if (timeInMinutes >= regularMarketStart && timeInMinutes < regularMarketEnd) {
return 'REGULAR';
}
else if (timeInMinutes >= regularMarketEnd && timeInMinutes < afterHoursEnd) {
return 'POST';
}
else {
return 'CLOSED';
}
}
/**
* Determine if intraday endpoint should be used based on market state
* Uses intraday for PRE, REGULAR, and POST market states
* Uses EOD for CLOSED state to save API credits
*
* @param marketState - Current market state
* @returns true if intraday endpoint should be used
*/
shouldUseIntradayEndpoint(marketState) {
return marketState !== 'CLOSED';
}
/**
* Format ticker symbol for intraday endpoints
* Marketstack intraday API requires periods to be replaced with hyphens
* Example: BRK.B → BRK-B
*
* @param symbol - Original ticker symbol
* @returns Formatted symbol for intraday endpoints
*/
formatSymbolForIntraday(symbol) {
return symbol.replace(/\./g, '-');
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJvdmlkZXIubWFya2V0c3RhY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9zdG9ja3MvcHJvdmlkZXJzL3Byb3ZpZGVyLm1hcmtldHN0YWNrLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFXNUM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQThCRztBQUNILE1BQU0sT0FBTyxtQkFBbUI7SUFhTTtJQVo3QixJQUFJLEdBQUcsYUFBYSxDQUFDO0lBQ3JCLFFBQVEsR0FBRyxFQUFFLENBQUMsQ0FBQywrRUFBK0U7SUFDckYsWUFBWSxHQUFHLElBQUksQ0FBQztJQUNwQixTQUFTLEdBQUc7UUFDMUIsaUJBQWlCLEVBQUUsQ0FBQyxFQUFFLGdDQUFnQztRQUN0RCxjQUFjLEVBQUUsU0FBUyxDQUFDLGlCQUFpQjtLQUM1QyxDQUFDO0lBRU0sTUFBTSxHQUFHLE9BQU8sQ0FBQztJQUNqQixPQUFPLEdBQUcsZ0NBQWdDLENBQUM7SUFDM0MsTUFBTSxDQUFTO0lBRXZCLFlBQVksTUFBYyxFQUFVLE1BQXdCO1FBQXhCLFdBQU0sR0FBTixNQUFNLENBQWtCO1FBQzFELElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNaLE1BQU0sSUFBSSxLQUFLLENBQUMsOENBQThDLENBQUMsQ0FBQztRQUNsRSxDQUFDO1FBRUQsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7SUFDdkIsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLFNBQVMsQ0FBQyxPQUEwQjtRQUMvQyxRQUFRLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUNyQixLQUFLLFNBQVM7Z0JBQ1osT0FBTyxJQUFJLENBQUMsaUJBQWlCLENBQUMsT0FBTyxDQUFDLENBQUM7WUFDekMsS0FBSyxZQUFZO2dCQUNmLE9BQU8sSUFBSSxDQUFDLHFCQUFxQixDQUFDLE9BQU8sQ0FBQyxDQUFD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