/** * Core Investment Analyzer * Analyzes pitch decks and business plans to generate investment insights */ import * as fs from 'fs'; import * as path from 'path'; import { PitchDeck, InvestmentAnalysis, CompanyInfo, MarketAnalysis, ProductAnalysis, TeamAnalysis, BusinessModel, TractionMetrics, FinancialAnalysis, CompetitiveAnalysis, RiskAssessment, InvestmentTerms, InvestmentRecommendation, AnalysisConfig, } from './types'; export class InvestmentAnalyzer { private config: AnalysisConfig; private analysisHistory: Map = new Map(); constructor(config: AnalysisConfig) { this.config = config; this.loadHistory(); } /** * Analyze a pitch deck and generate investment analysis */ async analyze(pitchDeck: PitchDeck): Promise { console.log(`Analyzing ${pitchDeck.filename}...`); // Extract company information const company = await this.extractCompanyInfo(pitchDeck); // Perform 10-point framework analysis const market = await this.analyzeMarket(pitchDeck); const product = await this.analyzeProduct(pitchDeck); const team = await this.analyzeTeam(pitchDeck); const businessModel = await this.analyzeBusinessModel(pitchDeck); const traction = await this.analyzeTraction(pitchDeck); const financials = await this.analyzeFinancials(pitchDeck); const competition = await this.analyzeCompetition(pitchDeck); const risks = await this.assessRisks(pitchDeck); const terms = await this.extractInvestmentTerms(pitchDeck); // Calculate scores const scores = { market: market.score, product: product.score, team: team.score, businessModel: businessModel.score, traction: traction.score, financials: financials.score, competition: competition.score, risks: risks.score, }; const overallScore = this.calculateOverallScore(scores); // Generate recommendation const recommendation = this.generateRecommendation( overallScore, scores, market, team, traction, risks ); // Create executive summary const executiveSummary = this.generateExecutiveSummary( company, market, team, traction, recommendation ); // Extract highlights and concerns const highlights = this.extractHighlights( market, team, product, traction, businessModel ); const concerns = this.extractConcerns(risks, competition, financials); const analysis: InvestmentAnalysis = { company, analysisDate: new Date(), analyst: 'AI Investment Assistant', market, product, team, businessModel, traction, financials, competition, risks, terms, recommendation, overallScore, scores, executiveSummary, highlights, concerns, }; // Save to history if (this.config.saveHistory) { this.saveAnalysis(pitchDeck.filename, analysis); } return analysis; } /** * Extract company information from pitch deck */ private async extractCompanyInfo(pitchDeck: PitchDeck): Promise { // In a real implementation, this would use AI/NLP to extract company info // For now, return a placeholder structure return { name: this.extractCompanyName(pitchDeck.content), stage: this.detectStage(pitchDeck.content), industry: this.detectIndustry(pitchDeck.content), }; } /** * Analyze market opportunity */ private async analyzeMarket(pitchDeck: PitchDeck): Promise { const content = pitchDeck.content.toLowerCase(); // Extract market size indicators const tam = this.extractMarketSize(content, 'tam'); const sam = this.extractMarketSize(content, 'sam'); const growthRate = this.extractGrowthRate(content); // Score based on market attractiveness let score = 5; // baseline if (tam.includes('billion') || tam.includes('B')) score += 2; if (growthRate.includes('%') && parseFloat(growthRate) > 20) score += 1; return { tam: tam || 'Not specified', sam: sam || 'Not specified', som: 'To be calculated', growthRate: growthRate || 'Not specified', trends: this.extractTrends(content), dynamics: 'Market dynamics analysis pending', timing: 'Market timing analysis pending', score: Math.min(score, 10), notes: 'Extracted from pitch deck content', }; } /** * Analyze product/solution */ private async analyzeProduct(pitchDeck: PitchDeck): Promise { const content = pitchDeck.content; return { problemStatement: this.extractProblem(content), solution: this.extractSolution(content), uniqueness: 'Differentiation analysis pending', productMarketFit: this.assessProductMarketFit(content), technology: 'Technology assessment pending', intellectualProperty: 'IP assessment pending', score: 6, notes: 'Product analysis based on pitch deck', }; } /** * Analyze team */ private async analyzeTeam(pitchDeck: PitchDeck): Promise { const founders = this.extractFounders(pitchDeck.content); let score = 5; if (founders.length >= 2) score += 2; if (founders.some((f) => f.experience.includes('year'))) score += 1; return { founders, teamSize: founders.length, keyHires: [], advisors: [], experience: 'Team experience assessment pending', gaps: [], score: Math.min(score, 10), notes: 'Team analysis from pitch deck', }; } /** * Analyze business model */ private async analyzeBusinessModel(pitchDeck: PitchDeck): Promise { return { revenueStreams: this.extractRevenueStreams(pitchDeck.content), pricingModel: 'Pricing model analysis pending', unitEconomics: { cac: this.extractCAC(pitchDeck.content), ltv: this.extractLTV(pitchDeck.content), }, scalability: 'Scalability assessment pending', goToMarket: 'GTM strategy analysis pending', score: 6, notes: 'Business model from pitch deck', }; } /** * Analyze traction and metrics */ private async analyzeTraction(pitchDeck: PitchDeck): Promise { const content = pitchDeck.content; const revenue = this.extractRevenue(content); const users = this.extractUsers(content); const growthRate = this.extractGrowthRate(content); let score = 4; if (revenue) score += 2; if (users && parseInt(users.replace(/\D/g, '')) > 1000) score += 1; if (growthRate && parseFloat(growthRate) > 100) score += 2; return { users: users || 'Not specified', customers: 'To be extracted', revenue: revenue || 'Not specified', growthRate: growthRate || 'Not specified', kpis: {}, milestones: this.extractMilestones(content), score: Math.min(score, 10), notes: 'Traction metrics from pitch deck', }; } /** * Analyze financials */ private async analyzeFinancials(pitchDeck: PitchDeck): Promise { return { currentRevenue: this.extractRevenue(pitchDeck.content) || 'Not specified', projectedRevenue: [], burnRate: 'To be calculated', runway: 'To be calculated', profitabilityTimeline: 'To be assessed', useOfFunds: this.extractUseOfFunds(pitchDeck.content), capitalEfficiency: 'To be calculated', score: 6, notes: 'Financial analysis from pitch deck', }; } /** * Analyze competition */ private async analyzeCompetition( pitchDeck: PitchDeck ): Promise { const competitors = this.extractCompetitors(pitchDeck.content); return { directCompetitors: competitors, indirectCompetitors: [], competitiveAdvantages: this.extractCompetitiveAdvantages(pitchDeck.content), barriersToEntry: [], marketPosition: 'Market positioning analysis pending', differentiation: 'Differentiation analysis pending', score: 6, notes: 'Competitive analysis from pitch deck', }; } /** * Assess risks */ private async assessRisks(pitchDeck: PitchDeck): Promise { // Identify common startup risks const risks = { marketRisks: [ { description: 'Market timing risk', severity: 'medium' as const, likelihood: 'medium' as const, }, ], executionRisks: [ { description: 'Execution and scaling risk', severity: 'high' as const, likelihood: 'medium' as const, }, ], technologyRisks: [], teamRisks: [], financialRisks: [ { description: 'Capital requirements', severity: 'medium' as const, likelihood: 'high' as const, }, ], regulatoryRisks: [], }; return { ...risks, overallRiskLevel: 'medium', mitigation: ['Due diligence recommended', 'Reference checks advised'], score: 6, notes: 'Risk assessment from pitch deck analysis', }; } /** * Extract investment terms */ private async extractInvestmentTerms( pitchDeck: PitchDeck ): Promise { const content = pitchDeck.content; return { fundingAsk: this.extractFundingAsk(content), valuation: { preMoney: this.extractValuation(content, 'pre'), postMoney: this.extractValuation(content, 'post'), }, equityOffered: 'To be negotiated', investmentStructure: 'Structure to be determined', useOfFunds: this.extractUseOfFunds(content), currentRound: this.detectRound(content), existingInvestors: this.extractInvestors(content), notes: 'Terms extracted from pitch deck', }; } /** * Generate investment recommendation */ private generateRecommendation( overallScore: number, scores: any, market: MarketAnalysis, team: TeamAnalysis, traction: TractionMetrics, risks: RiskAssessment ): InvestmentRecommendation { let decision: 'strong-buy' | 'buy' | 'hold' | 'pass'; if (overallScore >= this.config.scoring.thresholds.strongBuy) { decision = 'strong-buy'; } else if (overallScore >= this.config.scoring.thresholds.buy) { decision = 'buy'; } else if (overallScore >= this.config.scoring.thresholds.hold) { decision = 'hold'; } else { decision = 'pass'; } const keyStrengths = []; const keyConcerns = []; // Identify strengths if (scores.market >= 7) keyStrengths.push('Strong market opportunity'); if (scores.team >= 7) keyStrengths.push('Experienced team'); if (scores.traction >= 7) keyStrengths.push('Proven traction'); // Identify concerns if (scores.team < 6) keyConcerns.push('Team composition needs strengthening'); if (scores.traction < 5) keyConcerns.push('Limited traction to date'); if (risks.overallRiskLevel === 'high') keyConcerns.push('High risk profile'); return { decision, investmentThesis: `Investment opportunity with ${overallScore.toFixed( 1 )}/10 overall score`, keyStrengths, keyConcerns, nextSteps: [ 'Deep dive due diligence', 'Founder interviews', 'Market validation', 'Reference checks', ], reasoning: `Based on comprehensive analysis across 10 key dimensions`, }; } /** * Generate executive summary */ private generateExecutiveSummary( company: CompanyInfo, market: MarketAnalysis, team: TeamAnalysis, traction: TractionMetrics, recommendation: InvestmentRecommendation ): string { return `${company.name} is a ${company.stage || 'startup'} in the ${ company.industry || 'technology' } sector. The company addresses a ${ market.tam } market opportunity. Current traction includes ${traction.revenue} in revenue and ${ traction.users } users. Team consists of ${ team.founders.length } founders with relevant experience. Recommendation: ${ recommendation.decision.toUpperCase() }.`; } /** * Calculate weighted overall score */ private calculateOverallScore(scores: any): number { const weights = this.config.scoring.weights; const weightedScore = scores.market * weights.market + scores.product * weights.product + scores.team * weights.team + scores.businessModel * weights.businessModel + scores.traction * weights.traction + scores.financials * weights.financials + scores.competition * weights.competition + scores.risks * weights.risks; return Math.round(weightedScore * 10) / 10; } /** * Extract highlights from analysis */ private extractHighlights(...analyses: any[]): string[] { const highlights: string[] = []; analyses.forEach((analysis) => { if (analysis.score >= 7) { highlights.push(`Strong ${analysis.constructor.name || 'performance'}`); } }); return highlights.slice(0, 5); } /** * Extract concerns from analysis */ private extractConcerns(...analyses: any[]): string[] { const concerns: string[] = []; analyses.forEach((analysis) => { if (analysis.score < 6) { concerns.push(`Concerns in ${analysis.constructor.name || 'area'}`); } }); return concerns.slice(0, 5); } // Helper extraction methods private extractCompanyName(content: string): string { // Simple extraction - in real implementation use NLP const lines = content.split('\n'); return lines[0]?.trim() || 'Company Name'; } private detectStage(content: string): any { const lower = content.toLowerCase(); if (lower.includes('pre-seed')) return 'pre-seed'; if (lower.includes('seed')) return 'seed'; if (lower.includes('series a')) return 'series-a'; if (lower.includes('series b')) return 'series-b'; return 'seed'; } private detectIndustry(content: string): string { const lower = content.toLowerCase(); if (lower.includes('fintech') || lower.includes('financial')) return 'Fintech'; if (lower.includes('health') || lower.includes('medical')) return 'Healthcare'; if (lower.includes('ai') || lower.includes('ml')) return 'AI/ML'; if (lower.includes('saas')) return 'SaaS'; return 'Technology'; } private extractMarketSize(content: string, type: 'tam' | 'sam'): string { const pattern = new RegExp(`${type}.*?\\$?([\\d.]+\\s*[BM]illion?)`, 'i'); const match = content.match(pattern); return match ? match[0] : ''; } private extractGrowthRate(content: string): string { const match = content.match(/(\d+)%\s*(growth|CAGR|YoY)/i); return match ? `${match[1]}%` : ''; } private extractTrends(content: string): string[] { // Simplified - real implementation would use NLP return ['Market trend analysis pending']; } private extractProblem(content: string): string { return 'Problem statement extraction pending'; } private extractSolution(content: string): string { return 'Solution description extraction pending'; } private assessProductMarketFit(content: string): string { return 'Product-market fit assessment pending'; } private extractFounders(content: string): any[] { return [ { name: 'Founder Name', title: 'CEO', background: 'Background pending', experience: 'Experience pending', }, ]; } private extractRevenueStreams(content: string): string[] { return ['Revenue stream analysis pending']; } private extractCAC(content: string): string | undefined { const match = content.match(/CAC.*?\$?(\d+)/i); return match ? `$${match[1]}` : undefined; } private extractLTV(content: string): string | undefined { const match = content.match(/LTV.*?\$?(\d+)/i); return match ? `$${match[1]}` : undefined; } private extractRevenue(content: string): string { const match = content.match(/\$?([\d.]+)\s*([MKB])\s*(revenue|ARR|MRR)/i); return match ? `$${match[1]}${match[2]}` : ''; } private extractUsers(content: string): string { const match = content.match(/([\d,]+)\s*(users|customers)/i); return match ? match[1] : ''; } private extractMilestones(content: string): string[] { return []; } private extractUseOfFunds(content: string): string[] { return ['Use of funds analysis pending']; } private extractCompetitors(content: string): any[] { return []; } private extractCompetitiveAdvantages(content: string): string[] { return ['Competitive advantage analysis pending']; } private extractFundingAsk(content: string): string { const match = content.match(/raising.*?\$?([\d.]+)\s*([MKB])/i); return match ? `$${match[1]}${match[2]}` : 'Not specified'; } private extractValuation(content: string, type: 'pre' | 'post'): string { const pattern = new RegExp(`${type}.*?money.*?\\$?([\\d.]+)\\s*([MKB])`, 'i'); const match = content.match(pattern); return match ? `$${match[1]}${match[2]}` : ''; } private detectRound(content: string): string { const lower = content.toLowerCase(); if (lower.includes('series a')) return 'Series A'; if (lower.includes('series b')) return 'Series B'; if (lower.includes('seed')) return 'Seed'; return 'Seed'; } private extractInvestors(content: string): string[] { return []; } /** * Save analysis to history */ private saveAnalysis(filename: string, analysis: InvestmentAnalysis): void { this.analysisHistory.set(filename, analysis); const outputDir = this.config.outputDirectory; if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } const historyPath = path.join(outputDir, 'analysis-history.json'); const history = Object.fromEntries(this.analysisHistory); fs.writeFileSync(historyPath, JSON.stringify(history, null, 2)); } /** * Load analysis history */ private loadHistory(): void { const historyPath = path.join(this.config.outputDirectory, 'analysis-history.json'); if (fs.existsSync(historyPath)) { try { const data = fs.readFileSync(historyPath, 'utf-8'); const history = JSON.parse(data); this.analysisHistory = new Map(Object.entries(history)); } catch (error) { console.warn('Could not load analysis history'); } } } /** * Get analysis history */ getHistory(filename?: string): InvestmentAnalysis | Map { if (filename) { return this.analysisHistory.get(filename)!; } return this.analysisHistory; } }