@baguskto/saham
Version:
MCP Server untuk data saham Indonesia (IDX) - Implementasi Node.js/TypeScript
164 lines • 7.23 kB
JavaScript
;
/**
* Web scraping data source implementation (fallback)
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebScrapingSource = void 0;
const axios_1 = __importDefault(require("axios"));
const cheerio = __importStar(require("cheerio"));
const base_1 = require("./base");
const config_1 = require("../config");
const logger_1 = require("../utils/logger");
const types_1 = require("../types");
class WebScrapingSource extends base_1.DataSource {
userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36';
constructor() {
const dsConfig = config_1.config.getDataSources().webScraping;
super('web_scraping', types_1.DataSourcePriority.MEDIUM, dsConfig.timeout);
}
async fetchPage(url) {
const response = await axios_1.default.get(url, {
timeout: this.timeout,
headers: {
'User-Agent': this.userAgent,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1'
}
});
return cheerio.load(response.data);
}
async getStockInfo(ticker) {
return this.makeRequest(`getStockInfo(${ticker})`, async () => {
// This is a simplified implementation
// In a real implementation, you would scrape from actual IDX or financial websites
logger_1.logger.warn(`Web scraping for stock info not fully implemented for ${ticker}`);
return null;
});
}
async getMarketOverview() {
return this.makeRequest('getMarketOverview', async () => {
try {
// Try to scrape from Google Finance for IHSG
const url = 'https://www.google.com/finance/quote/JKSE:IDX';
const $ = await this.fetchPage(url);
// This is a basic implementation - the actual selectors would need to be
// determined by inspecting the actual website structure
const priceElement = $('[data-last-price]').first();
const changeElement = $('[data-last-change]').first();
if (priceElement.length === 0) {
throw new Error('Could not find price data on page');
}
const currentValue = parseFloat(priceElement.attr('data-last-price') || '0');
const changeText = changeElement.text() || '';
const change = parseFloat(changeText.replace(/[^\d.-]/g, '')) || 0;
const changePercent = currentValue > 0 ? (change / (currentValue - change)) * 100 : 0;
return {
ihsgValue: currentValue,
ihsgChange: change,
ihsgChangePercent: changePercent,
tradingVolume: 0, // Not available from basic scraping
tradingValue: 0,
marketStatus: this.getMarketStatus(),
topGainers: [], // Would require additional scraping
topLosers: [],
foreignNetFlow: undefined,
lastUpdated: new Date()
};
}
catch (error) {
logger_1.logger.debug('Web scraping failed, returning null');
return null;
}
});
}
getMarketStatus() {
// Jakarta timezone (UTC+7)
const now = new Date();
const jakartaTime = new Date(now.getTime() + (7 * 60 * 60 * 1000));
const day = jakartaTime.getUTCDay();
if (day === 0 || day === 6) {
return 'closed';
}
const hours = jakartaTime.getUTCHours();
const minutes = jakartaTime.getUTCMinutes();
const timeInMinutes = hours * 60 + minutes;
const marketOpen = 9 * 60;
const marketClose = 15 * 60 + 50;
const preMarketStart = 8 * 60;
if (timeInMinutes >= marketOpen && timeInMinutes <= marketClose) {
return 'open';
}
else if (timeInMinutes >= preMarketStart && timeInMinutes < marketOpen) {
return 'pre-market';
}
else {
return 'closed';
}
}
async getHistoricalData(ticker, period) {
return this.makeRequest(`getHistoricalData(${ticker}, ${period})`, async () => {
// Historical data scraping is complex and would require
// scraping from multiple pages or using different sources
logger_1.logger.warn(`Web scraping for historical data not implemented for ${ticker}`);
return null;
});
}
async getSectorPerformance() {
return this.makeRequest('getSectorPerformance', async () => {
// Sector performance would require scraping from IDX website
// or other financial data providers
logger_1.logger.warn('Web scraping for sector performance not implemented');
return null;
});
}
async searchStocks(query) {
const result = await this.makeRequest(`searchStocks(${query})`, async () => {
// Stock search would require scraping from IDX website
// or implementing a local search from CSV data
logger_1.logger.warn(`Web scraping for stock search not implemented for query: ${query}`);
return [];
});
return result || [];
}
}
exports.WebScrapingSource = WebScrapingSource;
//# sourceMappingURL=web-scraper.js.map