libs ready i think
This commit is contained in:
parent
1b34da9a69
commit
9673ae70ef
9 changed files with 242 additions and 129 deletions
|
|
@ -6,7 +6,7 @@ import { cors } from 'hono/cors';
|
||||||
import { getLogger, setLoggerConfig, shutdownLoggers } from '@stock-bot/logger';
|
import { getLogger, setLoggerConfig, shutdownLoggers } from '@stock-bot/logger';
|
||||||
import type { QueueManager } from '@stock-bot/queue';
|
import type { QueueManager } from '@stock-bot/queue';
|
||||||
import { Shutdown } from '@stock-bot/shutdown';
|
import { Shutdown } from '@stock-bot/shutdown';
|
||||||
import { ProxyManager } from '@stock-bot/di';
|
import { ProxyManager } from '@stock-bot/utils';
|
||||||
import type { ServiceContainer } from '@stock-bot/di';
|
import type { ServiceContainer } from '@stock-bot/di';
|
||||||
// Local imports
|
// Local imports
|
||||||
import { setupServiceContainer } from './setup/database-setup';
|
import { setupServiceContainer } from './setup/database-setup';
|
||||||
|
|
|
||||||
|
|
@ -115,18 +115,13 @@ export async function setupServiceContainer(): Promise<ServiceContainer> {
|
||||||
// Register Cache
|
// Register Cache
|
||||||
container.register({
|
container.register({
|
||||||
name: 'cache',
|
name: 'cache',
|
||||||
factory: () => {
|
factory: async () => {
|
||||||
const pool = connectionFactory.createCache({
|
const pool = await connectionFactory.createCache({
|
||||||
name: 'default',
|
name: 'default',
|
||||||
config: {
|
config: {
|
||||||
redisConfig: {
|
|
||||||
host: dbConfig.dragonfly.host,
|
host: dbConfig.dragonfly.host,
|
||||||
port: dbConfig.dragonfly.port,
|
port: dbConfig.dragonfly.port,
|
||||||
db: dbConfig.dragonfly.db,
|
db: dbConfig.dragonfly.db,
|
||||||
},
|
|
||||||
keyPrefix: 'data-ingestion:',
|
|
||||||
ttl: 3600,
|
|
||||||
enableMetrics: true,
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return pool.client;
|
return pool.client;
|
||||||
|
|
@ -137,32 +132,13 @@ export async function setupServiceContainer(): Promise<ServiceContainer> {
|
||||||
// Register QueueManager
|
// Register QueueManager
|
||||||
container.register({
|
container.register({
|
||||||
name: 'queue',
|
name: 'queue',
|
||||||
factory: () => {
|
factory: async () => {
|
||||||
const pool = connectionFactory.createQueue({
|
const pool = await connectionFactory.createQueue({
|
||||||
name: 'default',
|
name: 'default',
|
||||||
config: {
|
config: {
|
||||||
redis: queueConfig?.redis || {
|
|
||||||
host: dbConfig.dragonfly.host,
|
host: dbConfig.dragonfly.host,
|
||||||
port: dbConfig.dragonfly.port,
|
port: dbConfig.dragonfly.port,
|
||||||
db: dbConfig.dragonfly.db || 1,
|
db: dbConfig.dragonfly.db || 1,
|
||||||
},
|
|
||||||
defaultQueueOptions: {
|
|
||||||
defaultJobOptions: queueConfig?.defaultJobOptions || {
|
|
||||||
attempts: 3,
|
|
||||||
backoff: {
|
|
||||||
type: 'exponential',
|
|
||||||
delay: 1000,
|
|
||||||
},
|
|
||||||
removeOnComplete: 10,
|
|
||||||
removeOnFail: 5,
|
|
||||||
},
|
|
||||||
workers: 2,
|
|
||||||
concurrency: 1,
|
|
||||||
enableMetrics: true,
|
|
||||||
enableDLQ: true,
|
|
||||||
},
|
|
||||||
enableScheduledJobs: true,
|
|
||||||
delayWorkerStart: true,
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return pool.client;
|
return pool.client;
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,9 @@ export class ConnectionFactory implements IConnectionFactory {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dispose: async () => {
|
dispose: async () => {
|
||||||
|
if (client && typeof client.disconnect === 'function') {
|
||||||
await client.disconnect();
|
await client.disconnect();
|
||||||
|
}
|
||||||
this.pools.delete(key);
|
this.pools.delete(key);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -116,7 +118,9 @@ export class ConnectionFactory implements IConnectionFactory {
|
||||||
metrics: client.getPoolMetrics(),
|
metrics: client.getPoolMetrics(),
|
||||||
health: async () => client.connected,
|
health: async () => client.connected,
|
||||||
dispose: async () => {
|
dispose: async () => {
|
||||||
|
if (client && typeof client.disconnect === 'function') {
|
||||||
await client.disconnect();
|
await client.disconnect();
|
||||||
|
}
|
||||||
this.pools.delete(key);
|
this.pools.delete(key);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -129,7 +133,7 @@ export class ConnectionFactory implements IConnectionFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createCache(poolConfig: CachePoolConfig): ConnectionPool<any> {
|
async createCache(poolConfig: CachePoolConfig): Promise<ConnectionPool<any>> {
|
||||||
const key = `cache:${poolConfig.name}`;
|
const key = `cache:${poolConfig.name}`;
|
||||||
|
|
||||||
if (this.pools.has(key)) {
|
if (this.pools.has(key)) {
|
||||||
|
|
@ -142,16 +146,51 @@ export class ConnectionFactory implements IConnectionFactory {
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// TODO: Implement cache creation with dynamic import
|
const { createCache } = await import('@stock-bot/cache');
|
||||||
throw new Error('Cache creation temporarily disabled');
|
|
||||||
|
|
||||||
|
const client = createCache({
|
||||||
|
redisConfig: poolConfig.config as any,
|
||||||
|
keyPrefix: 'app:',
|
||||||
|
ttl: 3600,
|
||||||
|
enableMetrics: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
await client.waitForReady(10000);
|
||||||
|
|
||||||
|
const pool: ConnectionPool<any> = {
|
||||||
|
name: poolConfig.name,
|
||||||
|
client,
|
||||||
|
metrics: {
|
||||||
|
created: new Date(),
|
||||||
|
totalConnections: 1,
|
||||||
|
activeConnections: 1,
|
||||||
|
idleConnections: 0,
|
||||||
|
waitingRequests: 0,
|
||||||
|
errors: 0,
|
||||||
|
},
|
||||||
|
health: async () => {
|
||||||
|
try {
|
||||||
|
await client.waitForReady(1000);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dispose: async () => {
|
||||||
|
// Cache provider manages its own connections
|
||||||
|
this.pools.delete(key);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
this.pools.set(key, pool);
|
||||||
|
return pool;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('Failed to create cache pool', { name: poolConfig.name, error });
|
this.logger.error('Failed to create cache pool', { name: poolConfig.name, error });
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createQueue(poolConfig: QueuePoolConfig): ConnectionPool<any> {
|
async createQueue(poolConfig: QueuePoolConfig): Promise<ConnectionPool<any>> {
|
||||||
const key = `queue:${poolConfig.name}`;
|
const key = `queue:${poolConfig.name}`;
|
||||||
|
|
||||||
if (this.pools.has(key)) {
|
if (this.pools.has(key)) {
|
||||||
|
|
@ -164,9 +203,46 @@ export class ConnectionFactory implements IConnectionFactory {
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// TODO: Implement queue creation with dynamic import
|
const { QueueManager } = await import('@stock-bot/queue');
|
||||||
throw new Error('Queue creation temporarily disabled');
|
|
||||||
|
|
||||||
|
const manager = QueueManager.initialize({
|
||||||
|
redis: poolConfig.config as any,
|
||||||
|
defaultQueueOptions: {
|
||||||
|
defaultJobOptions: {
|
||||||
|
removeOnComplete: 100,
|
||||||
|
removeOnFail: 50,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const pool: ConnectionPool<any> = {
|
||||||
|
name: poolConfig.name,
|
||||||
|
client: manager,
|
||||||
|
metrics: {
|
||||||
|
created: new Date(),
|
||||||
|
totalConnections: 1,
|
||||||
|
activeConnections: 1,
|
||||||
|
idleConnections: 0,
|
||||||
|
waitingRequests: 0,
|
||||||
|
errors: 0,
|
||||||
|
},
|
||||||
|
health: async () => {
|
||||||
|
try {
|
||||||
|
return true; // QueueManager doesn't have isHealthy method yet
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dispose: async () => {
|
||||||
|
if (manager && typeof manager.shutdown === 'function') {
|
||||||
|
await manager.shutdown();
|
||||||
|
}
|
||||||
|
this.pools.delete(key);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
this.pools.set(key, pool);
|
||||||
|
return pool;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('Failed to create queue manager', { name: poolConfig.name, error });
|
this.logger.error('Failed to create queue manager', { name: poolConfig.name, error });
|
||||||
throw error;
|
throw error;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* OperationContext - Unified context for handler operations
|
* OperationContext - Unified context for handler operations
|
||||||
*
|
|
||||||
* TEMPORARILY DISABLED to avoid circular dependencies during library build
|
|
||||||
* Will be re-enabled once all core libraries are built
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { getLogger, type Logger } from '@stock-bot/logger';
|
import { getLogger, type Logger } from '@stock-bot/logger';
|
||||||
|
|
@ -13,25 +10,41 @@ export interface OperationContextOptions {
|
||||||
operationName: string;
|
operationName: string;
|
||||||
parentLogger?: Logger;
|
parentLogger?: Logger;
|
||||||
container?: ServiceResolver;
|
container?: ServiceResolver;
|
||||||
|
metadata?: Record<string, any>;
|
||||||
|
traceId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OperationContext {
|
export class OperationContext {
|
||||||
public readonly logger: Logger;
|
public readonly logger: Logger;
|
||||||
|
public readonly traceId: string;
|
||||||
|
public readonly metadata: Record<string, any>;
|
||||||
private readonly container?: ServiceResolver;
|
private readonly container?: ServiceResolver;
|
||||||
|
private readonly startTime: Date;
|
||||||
|
|
||||||
constructor(options: OperationContextOptions) {
|
constructor(options: OperationContextOptions) {
|
||||||
this.container = options.container;
|
this.container = options.container;
|
||||||
this.logger = options.parentLogger || getLogger(`${options.handlerName}:${options.operationName}`);
|
this.metadata = options.metadata || {};
|
||||||
|
this.traceId = options.traceId || this.generateTraceId();
|
||||||
|
this.startTime = new Date();
|
||||||
|
|
||||||
|
this.logger = options.parentLogger || getLogger(`${options.handlerName}:${options.operationName}`, {
|
||||||
|
traceId: this.traceId,
|
||||||
|
metadata: this.metadata,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new OperationContext with automatic resource management
|
* Creates a new OperationContext with automatic resource management
|
||||||
* TEMPORARILY SIMPLIFIED - full implementation will be restored after build fixes
|
|
||||||
*/
|
*/
|
||||||
static create(
|
static create(
|
||||||
handlerName: string,
|
handlerName: string,
|
||||||
operationName: string,
|
operationName: string,
|
||||||
options: { container?: ServiceResolver; parentLogger?: Logger } = {}
|
options: {
|
||||||
|
container?: ServiceResolver;
|
||||||
|
parentLogger?: Logger;
|
||||||
|
metadata?: Record<string, any>;
|
||||||
|
traceId?: string;
|
||||||
|
} = {}
|
||||||
): OperationContext {
|
): OperationContext {
|
||||||
return new OperationContext({
|
return new OperationContext({
|
||||||
handlerName,
|
handlerName,
|
||||||
|
|
@ -41,21 +54,85 @@ export class OperationContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleanup method - simplified for now
|
* Resolve a service from the container
|
||||||
*/
|
*/
|
||||||
async dispose(): Promise<void> {
|
resolve<T>(serviceName: string): T {
|
||||||
// Cleanup will be implemented when dependencies are resolved
|
if (!this.container) {
|
||||||
|
throw new Error('No service container available');
|
||||||
|
}
|
||||||
|
return this.container.resolve<T>(serviceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create child context - simplified for now
|
* Resolve a service asynchronously from the container
|
||||||
*/
|
*/
|
||||||
createChild(operationName: string): OperationContext {
|
async resolveAsync<T>(serviceName: string): Promise<T> {
|
||||||
|
if (!this.container) {
|
||||||
|
throw new Error('No service container available');
|
||||||
|
}
|
||||||
|
return this.container.resolveAsync<T>(serviceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add metadata to the context
|
||||||
|
*/
|
||||||
|
addMetadata(key: string, value: any): void {
|
||||||
|
this.metadata[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get execution time in milliseconds
|
||||||
|
*/
|
||||||
|
getExecutionTime(): number {
|
||||||
|
return Date.now() - this.startTime.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log operation completion with metrics
|
||||||
|
*/
|
||||||
|
logCompletion(success: boolean, error?: Error): void {
|
||||||
|
const executionTime = this.getExecutionTime();
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
this.logger.info('Operation completed successfully', {
|
||||||
|
executionTime,
|
||||||
|
metadata: this.metadata,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.logger.error('Operation failed', {
|
||||||
|
executionTime,
|
||||||
|
error: error?.message,
|
||||||
|
stack: error?.stack,
|
||||||
|
metadata: this.metadata,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup method
|
||||||
|
*/
|
||||||
|
async dispose(): Promise<void> {
|
||||||
|
this.logCompletion(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create child context
|
||||||
|
*/
|
||||||
|
createChild(operationName: string, metadata?: Record<string, any>): OperationContext {
|
||||||
return new OperationContext({
|
return new OperationContext({
|
||||||
handlerName: 'child',
|
handlerName: 'child',
|
||||||
operationName,
|
operationName,
|
||||||
parentLogger: this.logger,
|
parentLogger: this.logger,
|
||||||
container: this.container,
|
container: this.container,
|
||||||
|
traceId: this.traceId,
|
||||||
|
metadata: { ...this.metadata, ...metadata },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a unique trace ID
|
||||||
|
*/
|
||||||
|
private generateTraceId(): string {
|
||||||
|
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -170,8 +170,8 @@ export function createServiceContainer(
|
||||||
|
|
||||||
container.register({
|
container.register({
|
||||||
name: 'cache',
|
name: 'cache',
|
||||||
factory: () => {
|
factory: async () => {
|
||||||
const pool = connectionFactory.createCache({
|
const pool = await connectionFactory.createCache({
|
||||||
name: 'default',
|
name: 'default',
|
||||||
config: {} as any, // Config injected by factory
|
config: {} as any, // Config injected by factory
|
||||||
});
|
});
|
||||||
|
|
@ -182,8 +182,8 @@ export function createServiceContainer(
|
||||||
|
|
||||||
container.register({
|
container.register({
|
||||||
name: 'queue',
|
name: 'queue',
|
||||||
factory: () => {
|
factory: async () => {
|
||||||
const pool = connectionFactory.createQueue({
|
const pool = await connectionFactory.createQueue({
|
||||||
name: 'default',
|
name: 'default',
|
||||||
config: {} as any, // Config injected by factory
|
config: {} as any, // Config injected by factory
|
||||||
});
|
});
|
||||||
|
|
@ -192,43 +192,10 @@ export function createServiceContainer(
|
||||||
singleton: true,
|
singleton: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Optional services - comment out for now to avoid circular dependencies
|
// Note: Additional services can be registered by individual applications as needed:
|
||||||
// These can be registered manually by apps that need them
|
// - ProxyManager: container.register({ name: 'proxyManager', factory: () => ProxyManager.getInstance() })
|
||||||
|
// - Browser: container.register({ name: 'browser', factory: () => Browser })
|
||||||
// // Register ProxyManager
|
// - HttpClient: container.register({ name: 'httpClient', factory: () => createHttpClient(...) })
|
||||||
// container.register({
|
|
||||||
// name: 'proxyManager',
|
|
||||||
// factory: async () => {
|
|
||||||
// const { ProxyManager } = await import('@stock-bot/utils');
|
|
||||||
// await ProxyManager.initialize();
|
|
||||||
// return ProxyManager.getInstance();
|
|
||||||
// },
|
|
||||||
// singleton: true,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// // Register Browser service
|
|
||||||
// container.register({
|
|
||||||
// name: 'browser',
|
|
||||||
// factory: async () => {
|
|
||||||
// const { Browser } = await import('@stock-bot/browser');
|
|
||||||
// return Browser;
|
|
||||||
// },
|
|
||||||
// singleton: true,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// // Register HttpClient with default configuration
|
|
||||||
// container.register({
|
|
||||||
// name: 'httpClient',
|
|
||||||
// factory: async () => {
|
|
||||||
// const { createHttpClient } = await import('@stock-bot/http');
|
|
||||||
// return createHttpClient({
|
|
||||||
// timeout: 30000,
|
|
||||||
// retries: 3,
|
|
||||||
// userAgent: 'stock-bot/1.0',
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// singleton: true,
|
|
||||||
// });
|
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
@ -60,8 +60,8 @@ export interface PoolMetrics {
|
||||||
export interface ConnectionFactory {
|
export interface ConnectionFactory {
|
||||||
createMongoDB(config: MongoDBPoolConfig): Promise<ConnectionPool<any>>;
|
createMongoDB(config: MongoDBPoolConfig): Promise<ConnectionPool<any>>;
|
||||||
createPostgreSQL(config: PostgreSQLPoolConfig): Promise<ConnectionPool<any>>;
|
createPostgreSQL(config: PostgreSQLPoolConfig): Promise<ConnectionPool<any>>;
|
||||||
createCache(config: CachePoolConfig): ConnectionPool<any>;
|
createCache(config: CachePoolConfig): Promise<ConnectionPool<any>>;
|
||||||
createQueue(config: QueuePoolConfig): ConnectionPool<any>;
|
createQueue(config: QueuePoolConfig): Promise<ConnectionPool<any>>;
|
||||||
getPool(type: 'mongodb' | 'postgres' | 'cache' | 'queue', name: string): ConnectionPool<any> | undefined;
|
getPool(type: 'mongodb' | 'postgres' | 'cache' | 'queue', name: string): ConnectionPool<any> | undefined;
|
||||||
listPools(): Array<{ type: string; name: string; metrics: PoolMetrics }>;
|
listPools(): Array<{ type: string; name: string; metrics: PoolMetrics }>;
|
||||||
disposeAll(): Promise<void>;
|
disposeAll(): Promise<void>;
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,37 @@ describe('DI Library', () => {
|
||||||
expect(disposed).toBe(true);
|
expect(disposed).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('OperationContext - enhanced functionality', () => {
|
||||||
|
const container = new ServiceContainer('test');
|
||||||
|
const context = OperationContext.create('test-handler', 'test-operation', {
|
||||||
|
container,
|
||||||
|
metadata: { userId: '123' },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(context).toBeDefined();
|
||||||
|
expect(context.logger).toBeDefined();
|
||||||
|
expect(context.traceId).toBeDefined();
|
||||||
|
expect(context.metadata.userId).toBe('123');
|
||||||
|
expect(context.getExecutionTime()).toBeGreaterThanOrEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('OperationContext - service resolution', () => {
|
||||||
|
const container = new ServiceContainer('test');
|
||||||
|
|
||||||
|
container.register({
|
||||||
|
name: 'testService',
|
||||||
|
factory: () => ({ value: 'resolved' }),
|
||||||
|
singleton: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const context = OperationContext.create('test-handler', 'test-operation', {
|
||||||
|
container,
|
||||||
|
});
|
||||||
|
|
||||||
|
const service = context.resolve<{ value: string }>('testService');
|
||||||
|
expect(service.value).toBe('resolved');
|
||||||
|
});
|
||||||
|
|
||||||
test('ConnectionFactory - creation', () => {
|
test('ConnectionFactory - creation', () => {
|
||||||
const factory = new ConnectionFactory({
|
const factory = new ConnectionFactory({
|
||||||
service: 'test',
|
service: 'test',
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,8 @@ export { handlerRegistry } from '@stock-bot/handlers';
|
||||||
// Batch processing
|
// Batch processing
|
||||||
export { processBatchJob, processItems } from './batch-processor';
|
export { processBatchJob, processItems } from './batch-processor';
|
||||||
|
|
||||||
// Queue factory functions
|
|
||||||
// QueueFactory removed - use QueueManager directly
|
|
||||||
|
|
||||||
// DLQ handling
|
// DLQ handling
|
||||||
export { DeadLetterQueueHandler, DeadLetterQueueHandler as DLQHandler } from './dlq-handler';
|
export { DeadLetterQueueHandler } from './dlq-handler';
|
||||||
|
|
||||||
// Metrics
|
// Metrics
|
||||||
export { QueueMetricsCollector } from './queue-metrics';
|
export { QueueMetricsCollector } from './queue-metrics';
|
||||||
|
|
|
||||||
|
|
@ -83,33 +83,22 @@ export {
|
||||||
coppockCurve
|
coppockCurve
|
||||||
} from './technical-indicators';
|
} from './technical-indicators';
|
||||||
export * from './risk-metrics';
|
export * from './risk-metrics';
|
||||||
// export * from './portfolio-analytics';
|
|
||||||
// export * from './options-pricing';
|
|
||||||
// export * from './position-sizing';
|
|
||||||
export * from './performance-metrics';
|
export * from './performance-metrics';
|
||||||
// export * from './market-statistics';
|
|
||||||
// export * from './volatility-models';
|
|
||||||
// export * from './correlation-analysis';
|
|
||||||
|
|
||||||
// TODO: Re-enable when performance-metrics and risk-metrics are fixed
|
// Convenience function for comprehensive portfolio analysis
|
||||||
// // Convenience function for comprehensive portfolio analysis
|
export function analyzePortfolio(
|
||||||
// export function analyzePortfolio(
|
_returns: number[],
|
||||||
// returns: number[],
|
_equityCurve: Array<{ value: number; date: Date }>,
|
||||||
// equityCurve: Array<{ value: number; date: Date }>,
|
_benchmarkReturns?: number[],
|
||||||
// benchmarkReturns?: number[],
|
_riskFreeRate: number = 0.02
|
||||||
// riskFreeRate: number = 0.02
|
): {
|
||||||
// ): {
|
performance: any;
|
||||||
// performance: PortfolioAnalysis;
|
risk: any;
|
||||||
// risk: RiskMetrics;
|
} {
|
||||||
// trades?: any;
|
// Note: Implementation depends on performance-metrics and risk-metrics
|
||||||
// drawdown?: any;
|
// This is a placeholder for the full implementation
|
||||||
// } {
|
return {
|
||||||
// const performance = calculateStrategyMetrics(equityCurve, benchmarkReturns, riskFreeRate);
|
performance: {},
|
||||||
// const equityValues = equityCurve.map(point => point.value);
|
risk: {},
|
||||||
// const risk = calculateRiskMetrics(returns, equityValues, benchmarkReturns, riskFreeRate);
|
};
|
||||||
|
}
|
||||||
// return {
|
|
||||||
// performance,
|
|
||||||
// risk,
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue