agentbnb 5.1.0 → 5.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,238 @@
1
+ /** Alpha Vantage API response types */
2
+
3
+ export interface AVOverview {
4
+ Symbol: string;
5
+ Name: string;
6
+ Description: string;
7
+ Sector: string;
8
+ Industry: string;
9
+ MarketCapitalization: string;
10
+ PERatio: string;
11
+ ForwardPE: string;
12
+ PEGRatio: string;
13
+ PriceToSalesRatioTTM: string;
14
+ PriceToBookRatio: string;
15
+ EVToEBITDA: string;
16
+ EVToRevenue: string;
17
+ GrossProfitTTM: string;
18
+ RevenueTTM: string;
19
+ OperatingMarginTTM: string;
20
+ ProfitMargin: string;
21
+ ReturnOnEquityTTM: string;
22
+ ReturnOnAssetsTTM: string;
23
+ DebtToEquity: string;
24
+ CurrentRatio: string;
25
+ QuickRatio: string;
26
+ OperatingCashflowTTM: string;
27
+ RevenuePerShareTTM: string;
28
+ EPS: string;
29
+ DilutedEPSTTM: string;
30
+ Beta: string;
31
+ '52WeekHigh': string;
32
+ '52WeekLow': string;
33
+ '50DayMovingAverage': string;
34
+ '200DayMovingAverage': string;
35
+ SharesOutstanding: string;
36
+ DividendYield: string;
37
+ ExDividendDate: string;
38
+ AnalystTargetPrice: string;
39
+ AnalystRatingStrongBuy: string;
40
+ AnalystRatingBuy: string;
41
+ AnalystRatingHold: string;
42
+ AnalystRatingSell: string;
43
+ AnalystRatingStrongSell: string;
44
+ }
45
+
46
+ export interface AVQuarterlyReport {
47
+ fiscalDateEnding: string;
48
+ reportedCurrency: string;
49
+ totalRevenue: string;
50
+ netIncome: string;
51
+ grossProfit: string;
52
+ ebit: string;
53
+ ebitda: string;
54
+ operatingIncome: string;
55
+ interestExpense: string;
56
+ researchAndDevelopment: string;
57
+ sellingGeneralAndAdministrative: string;
58
+ }
59
+
60
+ export interface AVAnnualReport {
61
+ fiscalDateEnding: string;
62
+ reportedCurrency: string;
63
+ totalRevenue: string;
64
+ netIncome: string;
65
+ grossProfit: string;
66
+ ebit: string;
67
+ ebitda: string;
68
+ operatingIncome: string;
69
+ interestExpense: string;
70
+ researchAndDevelopment: string;
71
+ sellingGeneralAndAdministrative: string;
72
+ }
73
+
74
+ export interface AVIncomeStatement {
75
+ symbol: string;
76
+ annualReports: AVAnnualReport[];
77
+ quarterlyReports: AVQuarterlyReport[];
78
+ }
79
+
80
+ export interface AVBalanceSheetReport {
81
+ fiscalDateEnding: string;
82
+ reportedCurrency: string;
83
+ totalAssets: string;
84
+ totalCurrentAssets: string;
85
+ totalNonCurrentAssets: string;
86
+ totalLiabilities: string;
87
+ totalCurrentLiabilities: string;
88
+ totalNonCurrentLiabilities: string;
89
+ totalShareholderEquity: string;
90
+ longTermDebt: string;
91
+ shortTermDebt: string;
92
+ cashAndCashEquivalentsAtCarryingValue: string;
93
+ currentNetReceivables: string;
94
+ inventory: string;
95
+ }
96
+
97
+ export interface AVBalanceSheet {
98
+ symbol: string;
99
+ annualReports: AVBalanceSheetReport[];
100
+ quarterlyReports: AVBalanceSheetReport[];
101
+ }
102
+
103
+ export interface AVCashFlowReport {
104
+ fiscalDateEnding: string;
105
+ reportedCurrency: string;
106
+ operatingCashflow: string;
107
+ capitalExpenditures: string;
108
+ cashflowFromInvestment: string;
109
+ cashflowFromFinancing: string;
110
+ netIncome: string;
111
+ dividendPayout: string;
112
+ changeInOperatingAssets: string;
113
+ changeInOperatingLiabilities: string;
114
+ }
115
+
116
+ export interface AVCashFlow {
117
+ symbol: string;
118
+ annualReports: AVCashFlowReport[];
119
+ quarterlyReports: AVCashFlowReport[];
120
+ }
121
+
122
+ export interface AVQuarterlyEarning {
123
+ fiscalDateEnding: string;
124
+ reportedDate: string;
125
+ reportedEPS: string;
126
+ estimatedEPS: string;
127
+ surprise: string;
128
+ surprisePercentage: string;
129
+ }
130
+
131
+ export interface AVEarnings {
132
+ symbol: string;
133
+ quarterlyEarnings: AVQuarterlyEarning[];
134
+ annualEarnings: Array<{ fiscalDateEnding: string; reportedEPS: string }>;
135
+ }
136
+
137
+ export interface AVDailyPrice {
138
+ date: string;
139
+ open: string;
140
+ high: string;
141
+ low: string;
142
+ close: string;
143
+ adjustedClose: string;
144
+ volume: string;
145
+ dividendAmount: string;
146
+ splitCoefficient: string;
147
+ }
148
+
149
+ export interface AVDailyTimeSeries {
150
+ 'Meta Data': {
151
+ '1. Information': string;
152
+ '2. Symbol': string;
153
+ '3. Last Refreshed': string;
154
+ '4. Output Size': string;
155
+ '5. Time Zone': string;
156
+ };
157
+ 'Time Series (Daily)': Record<string, {
158
+ '1. open': string;
159
+ '2. high': string;
160
+ '3. low': string;
161
+ '4. close': string;
162
+ '5. adjusted close': string;
163
+ '6. volume': string;
164
+ '7. dividend amount': string;
165
+ '8. split coefficient': string;
166
+ }>;
167
+ }
168
+
169
+ export interface AVRSIEntry {
170
+ RSI: string;
171
+ }
172
+
173
+ export interface AVMACDEntry {
174
+ MACD: string;
175
+ MACD_Hist: string;
176
+ MACD_Signal: string;
177
+ }
178
+
179
+ export interface AVBBandsEntry {
180
+ 'Real Upper Band': string;
181
+ 'Real Middle Band': string;
182
+ 'Real Lower Band': string;
183
+ }
184
+
185
+ export interface AVStochEntry {
186
+ SlowK: string;
187
+ SlowD: string;
188
+ }
189
+
190
+ export interface AVADXEntry {
191
+ ADX: string;
192
+ }
193
+
194
+ export interface AVIndicatorResponse<T> {
195
+ 'Meta Data': Record<string, string>;
196
+ 'Technical Analysis: RSI'?: Record<string, T>;
197
+ 'Technical Analysis: MACD'?: Record<string, T>;
198
+ 'Technical Analysis: BBANDS'?: Record<string, T>;
199
+ 'Technical Analysis: STOCH'?: Record<string, T>;
200
+ 'Technical Analysis: ADX'?: Record<string, T>;
201
+ }
202
+
203
+ export interface AVNewsTickerSentiment {
204
+ ticker: string;
205
+ relevance_score: string;
206
+ ticker_sentiment_score: string;
207
+ ticker_sentiment_label: string;
208
+ }
209
+
210
+ export interface AVNewsArticle {
211
+ title: string;
212
+ url: string;
213
+ time_published: string;
214
+ summary: string;
215
+ overall_sentiment_score: string;
216
+ overall_sentiment_label: string;
217
+ ticker_sentiment: AVNewsTickerSentiment[];
218
+ topics: Array<{ topic: string; relevance_score: string }>;
219
+ }
220
+
221
+ export interface AVNewsSentiment {
222
+ feed: AVNewsArticle[];
223
+ }
224
+
225
+ export interface AllAVData {
226
+ overview: AVOverview;
227
+ income: AVIncomeStatement;
228
+ balance: AVBalanceSheet;
229
+ cashflow: AVCashFlow;
230
+ earnings: AVEarnings;
231
+ daily: AVDailyPrice[];
232
+ rsi: AVRSIEntry[];
233
+ macd: AVMACDEntry[];
234
+ bbands: AVBBandsEntry[];
235
+ stoch: AVStochEntry[];
236
+ adx: AVADXEntry[];
237
+ news: AVNewsSentiment;
238
+ }
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI entry point for deep-stock-analyst.
4
+ * Usage: node dist/index.js --ticker IBM --depth standard --style hybrid
5
+ * Output: JSON to stdout (genesis-bot reads this)
6
+ *
7
+ * Exit codes:
8
+ * 0 = success, JSON on stdout
9
+ * 1 = invalid args or API error
10
+ * 2 = rate limit / daily limit reached
11
+ */
12
+
13
+ import { runAnalysis } from './orchestrator.js';
14
+ import type { InvestmentStyle } from './analysis/signal.js';
15
+
16
+ function parseArgs(): { ticker: string; depth: string; style: InvestmentStyle } | null {
17
+ const args = process.argv.slice(2);
18
+ const get = (flag: string): string | undefined => {
19
+ const i = args.indexOf(flag);
20
+ return i >= 0 ? args[i + 1] : undefined;
21
+ };
22
+
23
+ const ticker = get('--ticker');
24
+ const depth = get('--depth') ?? 'standard';
25
+ const style = (get('--style') ?? 'hybrid') as InvestmentStyle;
26
+
27
+ if (!ticker) {
28
+ console.error('Usage: node dist/index.js --ticker <TICKER> [--depth quick|standard|deep] [--style growth|value|momentum|hybrid]');
29
+ return null;
30
+ }
31
+
32
+ const validDepths = ['quick', 'standard', 'deep'];
33
+ const validStyles = ['growth', 'value', 'momentum', 'hybrid'];
34
+
35
+ if (!validDepths.includes(depth)) {
36
+ console.error(`Invalid depth: ${depth}. Must be one of: ${validDepths.join(', ')}`);
37
+ return null;
38
+ }
39
+ if (!validStyles.includes(style)) {
40
+ console.error(`Invalid style: ${style}. Must be one of: ${validStyles.join(', ')}`);
41
+ return null;
42
+ }
43
+
44
+ return { ticker, depth, style };
45
+ }
46
+
47
+ async function main(): Promise<void> {
48
+ const args = parseArgs();
49
+ if (!args) {
50
+ process.exit(1);
51
+ }
52
+
53
+ const apiKey = process.env['ALPHA_VANTAGE_API_KEY'];
54
+ if (!apiKey) {
55
+ console.error('Missing ALPHA_VANTAGE_API_KEY environment variable');
56
+ process.exit(1);
57
+ }
58
+
59
+ try {
60
+ const result = await runAnalysis({
61
+ ticker: args.ticker,
62
+ depth: args.depth as 'quick' | 'standard' | 'deep',
63
+ style: args.style,
64
+ apiKey,
65
+ });
66
+
67
+ // Output clean JSON to stdout (genesis-bot reads this)
68
+ process.stdout.write(JSON.stringify(result, null, 2));
69
+ process.stdout.write('\n');
70
+ process.exit(0);
71
+ } catch (err: unknown) {
72
+ const msg = err instanceof Error ? err.message : String(err);
73
+
74
+ if (msg.includes('rate limit')) {
75
+ console.error(`[rate-limit] ${msg}`);
76
+ process.exit(2);
77
+ }
78
+
79
+ console.error(`[error] ${msg}`);
80
+ process.exit(1);
81
+ }
82
+ }
83
+
84
+ main();
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Thesis generation via Gemini Flash.
3
+ * Uses genesis-bot's existing GOOGLE_API_KEY.
4
+ * Input: pre-computed CompositeSignal + raw data → structured thesis JSON.
5
+ * LLM interprets numbers — does NOT recalculate anything.
6
+ */
7
+
8
+ import { GoogleGenerativeAI } from '@google/generative-ai';
9
+ import type { ValuationScore } from '../analysis/valuation.js';
10
+ import type { TechnicalScore } from '../analysis/technicals.js';
11
+ import type { FinancialHealth } from '../analysis/financial-health.js';
12
+ import type { SentimentScore } from '../analysis/sentiment.js';
13
+ import type { CompositeSignal } from '../analysis/signal.js';
14
+ import type { AVOverview } from '../api/types.js';
15
+
16
+ export interface InvestmentThesis {
17
+ bull_case: string;
18
+ bear_case: string;
19
+ catalysts: string[];
20
+ risks: string[];
21
+ time_horizon: 'short_term' | 'medium_term' | 'long_term';
22
+ entry_strategy: string;
23
+ }
24
+
25
+ const FALLBACK_THESIS: InvestmentThesis = {
26
+ bull_case: 'Quantitative metrics indicate favorable risk/reward. See scores for details.',
27
+ bear_case: 'Monitor red flags and technical weakness signals before committing capital.',
28
+ catalysts: ['Earnings surprise', 'Sector rotation', 'Macro tailwinds'],
29
+ risks: ['Market volatility', 'Rate sensitivity', 'Execution risk'],
30
+ time_horizon: 'medium_term',
31
+ entry_strategy: 'Consider scaling in near support levels identified in technical analysis.',
32
+ };
33
+
34
+ export async function generateThesis(
35
+ ticker: string,
36
+ overview: AVOverview,
37
+ composite: CompositeSignal,
38
+ valuation: ValuationScore,
39
+ technicals: TechnicalScore,
40
+ financials: FinancialHealth,
41
+ sentiment: SentimentScore,
42
+ ): Promise<InvestmentThesis> {
43
+ const apiKey = process.env['GOOGLE_API_KEY'];
44
+ if (!apiKey) {
45
+ console.error('[thesis] GOOGLE_API_KEY not set — returning fallback thesis');
46
+ return FALLBACK_THESIS;
47
+ }
48
+
49
+ const prompt = `You are a senior equity analyst. Based on the pre-computed analysis below, write a concise investment thesis. CRITICAL: Do NOT recalculate any numbers. All numbers have been verified. Your job is to INTERPRET, not COMPUTE.
50
+
51
+ Ticker: ${ticker}
52
+ Company: ${overview.Name ?? ticker}
53
+ Sector: ${overview.Sector ?? 'N/A'}
54
+ Industry: ${overview.Industry ?? 'N/A'}
55
+
56
+ Signal: ${composite.signal} (confidence: ${(composite.confidence * 100).toFixed(0)}%)
57
+ Composite Score: ${composite.composite_score}/100
58
+
59
+ Valuation: ${valuation.verdict} (score: ${valuation.composite.toFixed(0)}/100)
60
+ - P/E: ${overview.PERatio}, PEG: ${overview.PEGRatio}
61
+ - FCF Yield score: ${valuation.fcf_yield_score.toFixed(0)}/100
62
+
63
+ Technical Regime: ${technicals.regime}
64
+ - Trend: ${technicals.trend_score.toFixed(0)}/100, Momentum: ${technicals.momentum_score.toFixed(0)}/100
65
+ - Active Signals: ${JSON.stringify(technicals.signals.map((s) => s.name))}
66
+
67
+ Financial Health: ${financials.composite.toFixed(0)}/100
68
+ - Growth score: ${financials.growth_score.toFixed(0)}/100, Revenue growth: ${financials.raw.revenueGrowthPct.toFixed(1)}% YoY
69
+ - Red Flags: ${JSON.stringify(financials.red_flags)}
70
+ - Green Flags: ${JSON.stringify(financials.green_flags)}
71
+
72
+ Sentiment: ${sentiment.composite.toFixed(0)}/100 (${sentiment.news_volume} articles, ${(sentiment.bullish_ratio * 100).toFixed(0)}% bullish)
73
+
74
+ Support: ${composite.support_levels.join(', ')}
75
+ Resistance: ${composite.resistance_levels.join(', ')}
76
+
77
+ Respond ONLY with valid JSON (no markdown fences):
78
+ {
79
+ "bull_case": "3-4 sentences with specific numbers only from the data above",
80
+ "bear_case": "3-4 sentences with specific numbers only from the data above",
81
+ "catalysts": ["upcoming event or condition 1", "event 2", "event 3"],
82
+ "risks": ["risk 1", "risk 2", "risk 3"],
83
+ "time_horizon": "short_term | medium_term | long_term",
84
+ "entry_strategy": "Specific entry approach given support/resistance levels above"
85
+ }`;
86
+
87
+ try {
88
+ const genAI = new GoogleGenerativeAI(apiKey);
89
+ const model = genAI.getGenerativeModel({ model: 'gemini-2.0-flash' });
90
+ const result = await model.generateContent(prompt);
91
+ const text = result.response.text().trim();
92
+
93
+ // Strip markdown fences if present
94
+ const clean = text.replace(/^```(?:json)?\n?/, '').replace(/\n?```$/, '').trim();
95
+ const parsed = JSON.parse(clean) as InvestmentThesis;
96
+ return parsed;
97
+ } catch (err) {
98
+ console.error('[thesis] Gemini call failed:', err);
99
+ return FALLBACK_THESIS;
100
+ }
101
+ }
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Orchestrator: ties together all 5 analysis modules + thesis.
3
+ * All heavy computation happens here (no LLM until thesis step).
4
+ */
5
+
6
+ import { fetchAllData } from './api/alpha-vantage.js';
7
+ import { calculateValuation } from './analysis/valuation.js';
8
+ import { analyzeTechnicals } from './analysis/technicals.js';
9
+ import { analyzeFinancialHealth } from './analysis/financial-health.js';
10
+ import { analyzeSentiment } from './analysis/sentiment.js';
11
+ import { generateCompositeSignal } from './analysis/signal.js';
12
+ import { generateThesis } from './llm/thesis.js';
13
+ import type { InvestmentStyle } from './analysis/signal.js';
14
+
15
+ export interface AnalysisOptions {
16
+ ticker: string;
17
+ depth?: 'quick' | 'standard' | 'deep';
18
+ style?: InvestmentStyle;
19
+ apiKey: string;
20
+ }
21
+
22
+ export interface AnalysisResult {
23
+ ticker: string;
24
+ analyzed_at: string;
25
+ depth: string;
26
+ style: string;
27
+
28
+ // Top-level verdict
29
+ signal: string;
30
+ confidence: number;
31
+ composite_score: number;
32
+
33
+ // Module results
34
+ valuation: {
35
+ verdict: string;
36
+ composite: number;
37
+ pe_score: number;
38
+ peg_score: number;
39
+ fcf_yield_score: number;
40
+ ev_ebitda_score: number;
41
+ ps_score: number;
42
+ raw: Record<string, number>;
43
+ };
44
+ technicals: {
45
+ regime: string;
46
+ composite: number;
47
+ trend_score: number;
48
+ momentum_score: number;
49
+ volatility_score: number;
50
+ strength_score: number;
51
+ signals: Array<{ type: string; name: string; strength: number; description: string }>;
52
+ raw: Record<string, number>;
53
+ };
54
+ financials: {
55
+ composite: number;
56
+ profitability_score: number;
57
+ growth_score: number;
58
+ leverage_score: number;
59
+ efficiency_score: number;
60
+ red_flags: string[];
61
+ green_flags: string[];
62
+ raw: Record<string, number>;
63
+ };
64
+ sentiment: {
65
+ composite: number;
66
+ news_sentiment: number;
67
+ news_volume: number;
68
+ bullish_ratio: number;
69
+ key_headlines: string[];
70
+ topic_breakdown: Record<string, number>;
71
+ };
72
+
73
+ // LLM thesis
74
+ thesis: {
75
+ bull_case: string;
76
+ bear_case: string;
77
+ catalysts: string[];
78
+ risks: string[];
79
+ time_horizon: string;
80
+ entry_strategy: string;
81
+ };
82
+
83
+ // Price levels
84
+ support_levels: number[];
85
+ resistance_levels: number[];
86
+ key_factors: string[];
87
+ risk_factors: string[];
88
+ data_completeness: number;
89
+
90
+ // Company metadata
91
+ company: {
92
+ name: string;
93
+ sector: string;
94
+ industry: string;
95
+ market_cap: string;
96
+ price: number;
97
+ analyst_target: number;
98
+ };
99
+ }
100
+
101
+ export async function runAnalysis(options: AnalysisOptions): Promise<AnalysisResult> {
102
+ const { ticker, depth = 'standard', style = 'hybrid', apiKey } = options;
103
+
104
+ // Step 1: Fetch all API data (12 calls, serialized)
105
+ const data = await fetchAllData(ticker, apiKey);
106
+
107
+ // Step 2: Compute all modules (pure math, no LLM)
108
+ const valuation = calculateValuation(data.overview);
109
+ const technicals = analyzeTechnicals(
110
+ data.daily, data.rsi, data.macd, data.bbands, data.stoch, data.adx,
111
+ );
112
+ const financials = analyzeFinancialHealth(
113
+ data.overview, data.income, data.balance, data.cashflow, data.earnings,
114
+ );
115
+ const sentiment = analyzeSentiment(data.news, ticker);
116
+ const composite = generateCompositeSignal(
117
+ valuation, technicals, financials, sentiment, data.daily, style,
118
+ );
119
+
120
+ // Step 3: LLM thesis (~200ms, Gemini Flash)
121
+ const thesis = await generateThesis(
122
+ ticker, data.overview, composite, valuation, technicals, financials, sentiment,
123
+ );
124
+
125
+ // Step 4: Assemble result
126
+ const price = parseFloat(data.daily[0]?.adjustedClose ?? data.daily[0]?.close ?? '0');
127
+ const analystTarget = parseFloat(data.overview.AnalystTargetPrice ?? '0');
128
+ const marketCap = parseFloat(data.overview.MarketCapitalization ?? '0');
129
+ const mcStr =
130
+ marketCap >= 1e12 ? `$${(marketCap / 1e12).toFixed(2)}T`
131
+ : marketCap >= 1e9 ? `$${(marketCap / 1e9).toFixed(2)}B`
132
+ : marketCap >= 1e6 ? `$${(marketCap / 1e6).toFixed(2)}M`
133
+ : `$${marketCap.toFixed(0)}`;
134
+
135
+ return {
136
+ ticker: ticker.toUpperCase(),
137
+ analyzed_at: new Date().toISOString(),
138
+ depth,
139
+ style,
140
+
141
+ signal: composite.signal,
142
+ confidence: composite.confidence,
143
+ composite_score: composite.composite_score,
144
+
145
+ valuation: {
146
+ verdict: valuation.verdict,
147
+ composite: parseFloat(valuation.composite.toFixed(1)),
148
+ pe_score: parseFloat(valuation.pe_score.toFixed(1)),
149
+ peg_score: parseFloat(valuation.peg_score.toFixed(1)),
150
+ fcf_yield_score: parseFloat(valuation.fcf_yield_score.toFixed(1)),
151
+ ev_ebitda_score: parseFloat(valuation.ev_ebitda_score.toFixed(1)),
152
+ ps_score: parseFloat(valuation.ps_score.toFixed(1)),
153
+ raw: {
154
+ pe: valuation.raw.pe,
155
+ peg: valuation.raw.peg,
156
+ ps: valuation.raw.ps,
157
+ fcf_yield_pct: parseFloat(valuation.raw.fcfYieldPct.toFixed(2)),
158
+ },
159
+ },
160
+
161
+ technicals: {
162
+ regime: technicals.regime,
163
+ composite: parseFloat(technicals.composite.toFixed(1)),
164
+ trend_score: parseFloat(technicals.trend_score.toFixed(1)),
165
+ momentum_score: parseFloat(technicals.momentum_score.toFixed(1)),
166
+ volatility_score: parseFloat(technicals.volatility_score.toFixed(1)),
167
+ strength_score: parseFloat(technicals.strength_score.toFixed(1)),
168
+ signals: technicals.signals,
169
+ raw: {
170
+ rsi: parseFloat(technicals.raw.rsi.toFixed(1)),
171
+ macd_hist: parseFloat(technicals.raw.macdHist.toFixed(4)),
172
+ adx: parseFloat(technicals.raw.adx.toFixed(1)),
173
+ bb_position: parseFloat(technicals.raw.bbPosition.toFixed(2)),
174
+ price,
175
+ sma20: parseFloat(technicals.raw.sma20.toFixed(2)),
176
+ sma50: parseFloat(technicals.raw.sma50.toFixed(2)),
177
+ sma200: parseFloat(technicals.raw.sma200.toFixed(2)),
178
+ },
179
+ },
180
+
181
+ financials: {
182
+ composite: parseFloat(financials.composite.toFixed(1)),
183
+ profitability_score: parseFloat(financials.profitability_score.toFixed(1)),
184
+ growth_score: parseFloat(financials.growth_score.toFixed(1)),
185
+ leverage_score: parseFloat(financials.leverage_score.toFixed(1)),
186
+ efficiency_score: parseFloat(financials.efficiency_score.toFixed(1)),
187
+ red_flags: financials.red_flags,
188
+ green_flags: financials.green_flags,
189
+ raw: {
190
+ gross_margin_pct: parseFloat(financials.raw.grossMarginPct.toFixed(1)),
191
+ operating_margin_pct: parseFloat(financials.raw.operatingMarginPct.toFixed(1)),
192
+ net_margin_pct: parseFloat(financials.raw.netMarginPct.toFixed(1)),
193
+ roe: parseFloat(financials.raw.roe.toFixed(1)),
194
+ debt_to_equity: parseFloat(financials.raw.debtToEquity.toFixed(2)),
195
+ current_ratio: parseFloat(financials.raw.currentRatio.toFixed(2)),
196
+ revenue_growth_pct: parseFloat(financials.raw.revenueGrowthPct.toFixed(1)),
197
+ earnings_growth_pct: parseFloat(financials.raw.earningsGrowthPct.toFixed(1)),
198
+ consecutive_beats: financials.raw.consecutiveBeats,
199
+ },
200
+ },
201
+
202
+ sentiment: {
203
+ composite: parseFloat(sentiment.composite.toFixed(1)),
204
+ news_sentiment: parseFloat(sentiment.news_sentiment.toFixed(3)),
205
+ news_volume: sentiment.news_volume,
206
+ bullish_ratio: parseFloat(sentiment.bullish_ratio.toFixed(2)),
207
+ key_headlines: sentiment.key_headlines,
208
+ topic_breakdown: sentiment.topic_breakdown,
209
+ },
210
+
211
+ thesis,
212
+
213
+ support_levels: composite.support_levels,
214
+ resistance_levels: composite.resistance_levels,
215
+ key_factors: composite.key_factors,
216
+ risk_factors: composite.risk_factors,
217
+ data_completeness: composite.data_completeness,
218
+
219
+ company: {
220
+ name: data.overview.Name ?? ticker,
221
+ sector: data.overview.Sector ?? 'N/A',
222
+ industry: data.overview.Industry ?? 'N/A',
223
+ market_cap: mcStr,
224
+ price,
225
+ analyst_target: analystTarget,
226
+ },
227
+ };
228
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "outDir": "dist",
10
+ "rootDir": "src",
11
+ "declaration": true,
12
+ "sourceMap": true,
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "noUncheckedIndexedAccess": true,
16
+ "noUnusedLocals": true,
17
+ "noUnusedParameters": true
18
+ },
19
+ "include": ["src/**/*.ts"],
20
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
21
+ }