tradingview-screener-ts
Version:
TypeScript port of TradingView Screener with 100% Python parity - Based on the original Python library by shner-elmo (https://github.com/shner-elmo/TradingView-Screener)
288 lines • 9.09 kB
JavaScript
"use strict";
/**
* Performance monitoring and optimization utilities
*
* This module provides advanced performance monitoring, caching, and optimization
* features to achieve world-class performance standards.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.bundleInfo = exports.MemoryMonitor = exports.rateLimiter = exports.RateLimiter = exports.monitored = exports.performanceMonitor = exports.PerformanceMonitor = void 0;
/**
* Advanced performance monitor with caching and metrics
*/
class PerformanceMonitor {
constructor() {
this.cache = new Map();
this.metrics = [];
this.defaultTTL = 5 * 60 * 1000; // 5 minutes
}
/**
* Execute a function with performance monitoring
*/
async monitor(key, fn, options = {}) {
const startTime = performance.now();
const { ttl = this.defaultTTL, enableCache = true } = options;
// Check cache first
if (enableCache) {
const cached = this.getFromCache(key);
if (cached) {
const totalTime = performance.now() - startTime;
const metrics = {
queryTime: 0,
networkTime: 0,
parseTime: totalTime,
totalTime,
cacheHit: true,
dataSize: JSON.stringify(cached).length,
requestCount: 0,
};
this.metrics.push(metrics);
return { data: cached, metrics };
}
}
// Execute function with timing
const networkStart = performance.now();
const data = await fn();
const networkEnd = performance.now();
const parseStart = performance.now();
// Simulate parse time (actual parsing happens in the function)
const parseEnd = performance.now();
const totalTime = performance.now() - startTime;
// Cache the result
if (enableCache) {
this.setCache(key, data, ttl);
}
const metrics = {
queryTime: networkEnd - networkStart,
networkTime: networkEnd - networkStart,
parseTime: parseEnd - parseStart,
totalTime,
cacheHit: false,
dataSize: JSON.stringify(data).length,
requestCount: 1,
};
this.metrics.push(metrics);
return { data, metrics };
}
/**
* Get data from cache if valid
*/
getFromCache(key) {
const entry = this.cache.get(key);
if (!entry)
return null;
const now = Date.now();
if (now - entry.timestamp > entry.ttl) {
this.cache.delete(key);
return null;
}
entry.hits++;
return entry.data;
}
/**
* Set data in cache
*/
setCache(key, data, ttl) {
this.cache.set(key, {
data,
timestamp: Date.now(),
ttl,
hits: 0,
});
// Cleanup old entries (simple LRU)
if (this.cache.size > 100) {
const oldestKey = this.cache.keys().next().value;
this.cache.delete(oldestKey);
}
}
/**
* Get performance statistics
*/
getStats() {
if (this.metrics.length === 0) {
return {
averageQueryTime: 0,
averageNetworkTime: 0,
averageTotalTime: 0,
cacheHitRate: 0,
totalRequests: 0,
averageDataSize: 0,
};
}
const totalQueryTime = this.metrics.reduce((sum, m) => sum + m.queryTime, 0);
const totalNetworkTime = this.metrics.reduce((sum, m) => sum + m.networkTime, 0);
const totalTime = this.metrics.reduce((sum, m) => sum + m.totalTime, 0);
const cacheHits = this.metrics.filter(m => m.cacheHit).length;
const totalDataSize = this.metrics.reduce((sum, m) => sum + m.dataSize, 0);
return {
averageQueryTime: totalQueryTime / this.metrics.length,
averageNetworkTime: totalNetworkTime / this.metrics.length,
averageTotalTime: totalTime / this.metrics.length,
cacheHitRate: cacheHits / this.metrics.length,
totalRequests: this.metrics.length,
averageDataSize: totalDataSize / this.metrics.length,
};
}
/**
* Clear cache and metrics
*/
clear() {
this.cache.clear();
this.metrics.length = 0;
}
/**
* Get cache statistics
*/
getCacheStats() {
const now = Date.now();
const entries = Array.from(this.cache.entries()).map(([key, entry]) => ({
key,
hits: entry.hits,
age: now - entry.timestamp,
}));
const totalHits = entries.reduce((sum, entry) => sum + entry.hits, 0);
const totalAccesses = totalHits + this.metrics.filter(m => !m.cacheHit).length;
return {
size: this.cache.size,
hitRate: totalAccesses > 0 ? totalHits / totalAccesses : 0,
totalHits,
entries,
};
}
}
exports.PerformanceMonitor = PerformanceMonitor;
/**
* Global performance monitor instance
*/
exports.performanceMonitor = new PerformanceMonitor();
/**
* Performance decorator for methods
*/
function monitored(key) {
return function (target, propertyName, descriptor) {
const method = descriptor.value;
const monitorKey = key || `${target.constructor.name}.${propertyName}`;
descriptor.value = async function (...args) {
const result = await exports.performanceMonitor.monitor(`${monitorKey}-${JSON.stringify(args).slice(0, 100)}`, () => method.apply(this, args));
return result.data;
};
return descriptor;
};
}
exports.monitored = monitored;
/**
* Request rate limiter
*/
class RateLimiter {
constructor(maxRequests = 100, timeWindowMs = 60000) {
this.requests = [];
this.maxRequests = maxRequests;
this.timeWindow = timeWindowMs;
}
/**
* Check if request is allowed
*/
isAllowed() {
const now = Date.now();
// Remove old requests outside time window
this.requests = this.requests.filter(time => now - time < this.timeWindow);
if (this.requests.length >= this.maxRequests) {
return false;
}
this.requests.push(now);
return true;
}
/**
* Wait until next request is allowed
*/
async waitForNext() {
while (!this.isAllowed()) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
/**
* Get current rate limit status
*/
getStatus() {
const now = Date.now();
this.requests = this.requests.filter(time => now - time < this.timeWindow);
const remaining = Math.max(0, this.maxRequests - this.requests.length);
const oldestRequest = this.requests[0];
const resetTime = oldestRequest ? oldestRequest + this.timeWindow : now;
return {
remaining,
resetTime,
isLimited: remaining === 0,
};
}
}
exports.RateLimiter = RateLimiter;
/**
* Global rate limiter instance
*/
exports.rateLimiter = new RateLimiter();
/**
* Memory usage monitor
*/
class MemoryMonitor {
/**
* Get current memory usage
*/
static getUsage() {
if (typeof process !== 'undefined' && process.memoryUsage) {
// Node.js environment
const usage = process.memoryUsage();
return {
used: usage.heapUsed,
total: usage.heapTotal,
percentage: (usage.heapUsed / usage.heapTotal) * 100,
heapUsed: usage.heapUsed,
heapTotal: usage.heapTotal,
};
}
else if (typeof performance !== 'undefined' && performance.memory) {
// Browser environment
const memory = performance.memory;
return {
used: memory.usedJSHeapSize,
total: memory.totalJSHeapSize,
percentage: (memory.usedJSHeapSize / memory.totalJSHeapSize) * 100,
};
}
else {
// Fallback
return {
used: 0,
total: 0,
percentage: 0,
};
}
}
/**
* Check if memory usage is high
*/
static isHighUsage(threshold = 80) {
return this.getUsage().percentage > threshold;
}
}
exports.MemoryMonitor = MemoryMonitor;
/**
* Bundle size analyzer
*/
exports.bundleInfo = {
version: '1.0.0',
estimatedSize: {
minified: '~45KB',
gzipped: '~12KB',
brotli: '~10KB',
},
dependencies: {
runtime: ['axios'],
development: ['typescript', 'jest', 'eslint', 'prettier'],
},
treeShaking: true,
esModules: true,
commonjs: true,
umd: true,
};
//# sourceMappingURL=performance.js.map