market-data-analyzer 2.0.0

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.

Potentially problematic release.


This version of market-data-analyzer might be problematic. Click here for more details.

Files changed (58) hide show
  1. package/README.md +159 -0
  2. package/dist/index.d.ts +12 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +267 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/tools/analyze_portfolio.d.ts +14 -0
  7. package/dist/tools/analyze_portfolio.d.ts.map +1 -0
  8. package/dist/tools/analyze_portfolio.js +155 -0
  9. package/dist/tools/analyze_portfolio.js.map +1 -0
  10. package/dist/tools/analyze_stock.d.ts +8 -0
  11. package/dist/tools/analyze_stock.d.ts.map +1 -0
  12. package/dist/tools/analyze_stock.js +211 -0
  13. package/dist/tools/analyze_stock.js.map +1 -0
  14. package/dist/tools/compare_assets.d.ts +8 -0
  15. package/dist/tools/compare_assets.d.ts.map +1 -0
  16. package/dist/tools/compare_assets.js +138 -0
  17. package/dist/tools/compare_assets.js.map +1 -0
  18. package/dist/tools/crypto_analysis.d.ts +8 -0
  19. package/dist/tools/crypto_analysis.d.ts.map +1 -0
  20. package/dist/tools/crypto_analysis.js +192 -0
  21. package/dist/tools/crypto_analysis.js.map +1 -0
  22. package/dist/tools/market_overview.d.ts +8 -0
  23. package/dist/tools/market_overview.d.ts.map +1 -0
  24. package/dist/tools/market_overview.js +223 -0
  25. package/dist/tools/market_overview.js.map +1 -0
  26. package/dist/tools/screen_stocks.d.ts +19 -0
  27. package/dist/tools/screen_stocks.d.ts.map +1 -0
  28. package/dist/tools/screen_stocks.js +122 -0
  29. package/dist/tools/screen_stocks.js.map +1 -0
  30. package/dist/types.d.ts +158 -0
  31. package/dist/types.d.ts.map +1 -0
  32. package/dist/types.js +5 -0
  33. package/dist/types.js.map +1 -0
  34. package/dist/utils/api.d.ts +37 -0
  35. package/dist/utils/api.d.ts.map +1 -0
  36. package/dist/utils/api.js +228 -0
  37. package/dist/utils/api.js.map +1 -0
  38. package/dist/utils/cache.d.ts +20 -0
  39. package/dist/utils/cache.d.ts.map +1 -0
  40. package/dist/utils/cache.js +52 -0
  41. package/dist/utils/cache.js.map +1 -0
  42. package/dist/utils/math.d.ts +30 -0
  43. package/dist/utils/math.d.ts.map +1 -0
  44. package/dist/utils/math.js +300 -0
  45. package/dist/utils/math.js.map +1 -0
  46. package/package.json +30 -0
  47. package/src/index.ts +329 -0
  48. package/src/tools/analyze_portfolio.ts +204 -0
  49. package/src/tools/analyze_stock.ts +204 -0
  50. package/src/tools/compare_assets.ts +181 -0
  51. package/src/tools/crypto_analysis.ts +221 -0
  52. package/src/tools/market_overview.ts +236 -0
  53. package/src/tools/screen_stocks.ts +154 -0
  54. package/src/types.ts +175 -0
  55. package/src/utils/api.ts +262 -0
  56. package/src/utils/cache.ts +65 -0
  57. package/src/utils/math.ts +332 -0
  58. package/tsconfig.json +19 -0
@@ -0,0 +1,236 @@
1
+ /**
2
+ * market_overview -- Current market snapshot.
3
+ *
4
+ * Fetches live data for major indices, sector ETFs, and VIX
5
+ * from Yahoo Finance to build a comprehensive market summary.
6
+ */
7
+
8
+ import { yahooQuote } from "../utils/api.js";
9
+ import { formatCurrency, formatVolume } from "../utils/math.js";
10
+
11
+ /** Index/ETF symbols to track */
12
+ const INDEX_SYMBOLS = [
13
+ "^GSPC", // S&P 500
14
+ "^IXIC", // NASDAQ Composite
15
+ "^DJI", // Dow Jones
16
+ "^RUT", // Russell 2000
17
+ "^VIX", // VIX
18
+ "^TNX", // 10-Year Treasury Yield
19
+ ];
20
+
21
+ /** Sector ETFs (SPDR Select Sector) */
22
+ const SECTOR_ETFS = [
23
+ { symbol: "XLK", name: "Technology" },
24
+ { symbol: "XLV", name: "Healthcare" },
25
+ { symbol: "XLF", name: "Financials" },
26
+ { symbol: "XLY", name: "Consumer Discretionary" },
27
+ { symbol: "XLP", name: "Consumer Staples" },
28
+ { symbol: "XLE", name: "Energy" },
29
+ { symbol: "XLI", name: "Industrials" },
30
+ { symbol: "XLU", name: "Utilities" },
31
+ { symbol: "XLRE", name: "Real Estate" },
32
+ { symbol: "XLB", name: "Materials" },
33
+ { symbol: "XLC", name: "Communication Services" },
34
+ ];
35
+
36
+ /** Other reference symbols */
37
+ const OTHER_SYMBOLS = [
38
+ "BTC-USD", // Bitcoin
39
+ "ETH-USD", // Ethereum
40
+ "GC=F", // Gold futures
41
+ "CL=F", // Crude oil WTI
42
+ ];
43
+
44
+ export async function handleMarketOverview(): Promise<string> {
45
+ // Fetch all data in parallel
46
+ const allSymbols = [
47
+ ...INDEX_SYMBOLS,
48
+ ...SECTOR_ETFS.map((s) => s.symbol),
49
+ ...OTHER_SYMBOLS,
50
+ ];
51
+
52
+ const quotes = await yahooQuote(allSymbols);
53
+ const quoteMap = new Map(quotes.map((q) => [q.symbol, q]));
54
+
55
+ const lines: string[] = [];
56
+
57
+ lines.push("# Market Overview");
58
+ lines.push("");
59
+ lines.push(`*Live data as of ${new Date().toISOString().slice(0, 16)} UTC*`);
60
+ lines.push("");
61
+
62
+ // Major Indices
63
+ lines.push("## Major Indices");
64
+ lines.push("");
65
+ lines.push("| Index | Value | Change | Change % |");
66
+ lines.push("|-------|-------|--------|----------|");
67
+
68
+ const indexNames: Record<string, string> = {
69
+ "^GSPC": "S&P 500",
70
+ "^IXIC": "NASDAQ Composite",
71
+ "^DJI": "Dow Jones Industrial",
72
+ "^RUT": "Russell 2000",
73
+ "^VIX": "CBOE Volatility (VIX)",
74
+ "^TNX": "10-Year Treasury Yield",
75
+ };
76
+
77
+ for (const sym of INDEX_SYMBOLS) {
78
+ const q = quoteMap.get(sym);
79
+ if (!q || q.regularMarketPrice == null) continue;
80
+ const name = indexNames[sym] ?? q.shortName ?? sym;
81
+ const price = q.regularMarketPrice;
82
+ const change = q.regularMarketChange ?? 0;
83
+ const changePct = q.regularMarketChangePercent ?? 0;
84
+ const sign = change >= 0 ? "+" : "";
85
+ const fmtPrice = price > 100
86
+ ? price.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
87
+ : price.toFixed(2);
88
+ lines.push(`| ${name} | ${fmtPrice} | ${sign}${change.toFixed(2)} | ${sign}${changePct.toFixed(2)}% |`);
89
+ }
90
+ lines.push("");
91
+
92
+ // VIX interpretation
93
+ const vix = quoteMap.get("^VIX");
94
+ if (vix?.regularMarketPrice != null) {
95
+ const vixVal = vix.regularMarketPrice;
96
+ lines.push("### VIX Interpretation");
97
+ lines.push("");
98
+ if (vixVal < 15) {
99
+ lines.push(`VIX at ${vixVal.toFixed(2)} -- **Low volatility.** Market complacency; conditions are calm.`);
100
+ } else if (vixVal < 20) {
101
+ lines.push(`VIX at ${vixVal.toFixed(2)} -- **Normal.** Typical market conditions.`);
102
+ } else if (vixVal < 25) {
103
+ lines.push(`VIX at ${vixVal.toFixed(2)} -- **Elevated.** Increasing uncertainty and hedging activity.`);
104
+ } else if (vixVal < 30) {
105
+ lines.push(`VIX at ${vixVal.toFixed(2)} -- **High.** Significant fear in the market.`);
106
+ } else {
107
+ lines.push(`VIX at ${vixVal.toFixed(2)} -- **Very high.** Extreme fear; possible market stress event.`);
108
+ }
109
+ lines.push("");
110
+ }
111
+
112
+ // Sector Performance
113
+ lines.push("## Sector Performance");
114
+ lines.push("");
115
+ lines.push("| Sector | ETF | Price | Change % |");
116
+ lines.push("|--------|-----|-------|----------|");
117
+
118
+ const sectorData: Array<{ name: string; symbol: string; changePct: number }> = [];
119
+ for (const etf of SECTOR_ETFS) {
120
+ const q = quoteMap.get(etf.symbol);
121
+ if (!q || q.regularMarketPrice == null) continue;
122
+ const changePct = q.regularMarketChangePercent ?? 0;
123
+ const sign = changePct >= 0 ? "+" : "";
124
+ lines.push(`| ${etf.name} | ${etf.symbol} | $${q.regularMarketPrice.toFixed(2)} | ${sign}${changePct.toFixed(2)}% |`);
125
+ sectorData.push({ name: etf.name, symbol: etf.symbol, changePct });
126
+ }
127
+ lines.push("");
128
+
129
+ // Sector breadth
130
+ if (sectorData.length > 0) {
131
+ const positive = sectorData.filter((s) => s.changePct > 0).length;
132
+ const negative = sectorData.filter((s) => s.changePct < 0).length;
133
+ const bestSector = [...sectorData].sort((a, b) => b.changePct - a.changePct)[0];
134
+ const worstSector = [...sectorData].sort((a, b) => a.changePct - b.changePct)[0];
135
+
136
+ lines.push("### Sector Breadth");
137
+ lines.push("");
138
+ lines.push(`- Sectors advancing: ${positive}/${sectorData.length}`);
139
+ lines.push(`- Sectors declining: ${negative}/${sectorData.length}`);
140
+ lines.push(`- Best: **${bestSector.name}** (${bestSector.changePct >= 0 ? "+" : ""}${bestSector.changePct.toFixed(2)}%)`);
141
+ lines.push(`- Worst: **${worstSector.name}** (${worstSector.changePct >= 0 ? "+" : ""}${worstSector.changePct.toFixed(2)}%)`);
142
+ lines.push("");
143
+ }
144
+
145
+ // Crypto & Commodities
146
+ lines.push("## Crypto & Commodities");
147
+ lines.push("");
148
+ lines.push("| Asset | Price | Change % |");
149
+ lines.push("|-------|-------|----------|");
150
+
151
+ const otherNames: Record<string, string> = {
152
+ "BTC-USD": "Bitcoin",
153
+ "ETH-USD": "Ethereum",
154
+ "GC=F": "Gold",
155
+ "CL=F": "Crude Oil WTI",
156
+ };
157
+
158
+ for (const sym of OTHER_SYMBOLS) {
159
+ const q = quoteMap.get(sym);
160
+ if (!q || q.regularMarketPrice == null) continue;
161
+ const name = otherNames[sym] ?? q.shortName ?? sym;
162
+ const changePct = q.regularMarketChangePercent ?? 0;
163
+ const sign = changePct >= 0 ? "+" : "";
164
+ lines.push(`| ${name} | $${q.regularMarketPrice.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })} | ${sign}${changePct.toFixed(2)}% |`);
165
+ }
166
+ lines.push("");
167
+
168
+ // Market Breadth / Sentiment estimate
169
+ lines.push("## Market Sentiment");
170
+ lines.push("");
171
+
172
+ const spx = quoteMap.get("^GSPC");
173
+ const spxChange = spx?.regularMarketChangePercent ?? 0;
174
+ const vixVal = vix?.regularMarketPrice ?? 20;
175
+
176
+ let sentimentScore = 50;
177
+
178
+ // SPX contribution
179
+ if (spxChange > 1) sentimentScore += 15;
180
+ else if (spxChange > 0.5) sentimentScore += 10;
181
+ else if (spxChange > 0) sentimentScore += 5;
182
+ else if (spxChange < -1) sentimentScore -= 15;
183
+ else if (spxChange < -0.5) sentimentScore -= 10;
184
+ else if (spxChange < 0) sentimentScore -= 5;
185
+
186
+ // VIX contribution
187
+ if (vixVal < 15) sentimentScore += 10;
188
+ else if (vixVal < 20) sentimentScore += 5;
189
+ else if (vixVal > 30) sentimentScore -= 15;
190
+ else if (vixVal > 25) sentimentScore -= 10;
191
+ else if (vixVal > 20) sentimentScore -= 5;
192
+
193
+ // Sector breadth
194
+ if (sectorData.length > 0) {
195
+ const posRatio = sectorData.filter((s) => s.changePct > 0).length / sectorData.length;
196
+ if (posRatio > 0.7) sentimentScore += 10;
197
+ else if (posRatio > 0.5) sentimentScore += 5;
198
+ else if (posRatio < 0.3) sentimentScore -= 10;
199
+ }
200
+
201
+ sentimentScore = Math.max(0, Math.min(100, sentimentScore));
202
+ const sentiment = sentimentScore >= 60 ? "BULLISH" : sentimentScore <= 40 ? "BEARISH" : "NEUTRAL";
203
+
204
+ lines.push(`**Sentiment: ${sentiment}** (Score: ${sentimentScore}/100)`);
205
+ lines.push("");
206
+
207
+ // Key observations
208
+ lines.push("## Key Observations");
209
+ lines.push("");
210
+
211
+ if (spx) {
212
+ const val = spx.regularMarketPrice?.toLocaleString("en-US", { minimumFractionDigits: 2 }) ?? "N/A";
213
+ lines.push(`- S&P 500 at ${val} (${spxChange >= 0 ? "+" : ""}${spxChange.toFixed(2)}%).`);
214
+ }
215
+
216
+ const btc = quoteMap.get("BTC-USD");
217
+ if (btc && btc.regularMarketChangePercent != null && Math.abs(btc.regularMarketChangePercent) > 2) {
218
+ lines.push(`- Crypto ${btc.regularMarketChangePercent > 0 ? "rallying" : "selling off"}: Bitcoin ${btc.regularMarketChangePercent >= 0 ? "+" : ""}${btc.regularMarketChangePercent.toFixed(1)}%.`);
219
+ }
220
+
221
+ const oil = quoteMap.get("CL=F");
222
+ if (oil && oil.regularMarketChangePercent != null && Math.abs(oil.regularMarketChangePercent) > 1) {
223
+ lines.push(`- Oil ${oil.regularMarketChangePercent > 0 ? "rising" : "falling"} (${oil.regularMarketChangePercent >= 0 ? "+" : ""}${oil.regularMarketChangePercent.toFixed(1)}%).`);
224
+ }
225
+
226
+ const gold = quoteMap.get("GC=F");
227
+ if (gold && gold.regularMarketChangePercent != null && gold.regularMarketChangePercent > 0.5) {
228
+ lines.push(`- Gold up ${gold.regularMarketChangePercent.toFixed(1)}% -- possible flight to safety.`);
229
+ }
230
+
231
+ if (vixVal > 25) {
232
+ lines.push("- Elevated VIX suggests caution; hedging demand is high.");
233
+ }
234
+
235
+ return lines.join("\n");
236
+ }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * screen_stocks -- Screen stocks by criteria using Yahoo Finance data.
3
+ *
4
+ * Fetches live quotes for a universe of popular stocks and filters
5
+ * by market cap, P/E ratio, sector, volume, etc.
6
+ */
7
+
8
+ import { yahooScreener, POPULAR_SYMBOLS } from "../utils/api.js";
9
+ import { formatCurrency, formatVolume } from "../utils/math.js";
10
+ import type { YahooQuote } from "../types.js";
11
+
12
+ export interface ScreenCriteria {
13
+ min_market_cap?: number;
14
+ max_market_cap?: number;
15
+ min_pe?: number;
16
+ max_pe?: number;
17
+ sector?: string;
18
+ min_volume?: number;
19
+ min_price?: number;
20
+ max_price?: number;
21
+ limit?: number;
22
+ }
23
+
24
+ export async function handleScreenStocks(criteria: ScreenCriteria): Promise<string> {
25
+ // Fetch quotes for the full symbol universe
26
+ let quotes = await yahooScreener(POPULAR_SYMBOLS);
27
+
28
+ // Apply filters
29
+ if (criteria.min_market_cap !== undefined) {
30
+ quotes = quotes.filter((q) => (q.marketCap ?? 0) >= criteria.min_market_cap!);
31
+ }
32
+ if (criteria.max_market_cap !== undefined) {
33
+ quotes = quotes.filter((q) => (q.marketCap ?? Infinity) <= criteria.max_market_cap!);
34
+ }
35
+ if (criteria.min_pe !== undefined) {
36
+ quotes = quotes.filter((q) => q.trailingPE != null && q.trailingPE >= criteria.min_pe!);
37
+ }
38
+ if (criteria.max_pe !== undefined) {
39
+ quotes = quotes.filter((q) => q.trailingPE != null && q.trailingPE <= criteria.max_pe!);
40
+ }
41
+ if (criteria.sector !== undefined) {
42
+ const sectorLower = criteria.sector.toLowerCase();
43
+ quotes = quotes.filter((q) =>
44
+ (q.sector ?? "").toLowerCase().includes(sectorLower) ||
45
+ (q.industry ?? "").toLowerCase().includes(sectorLower)
46
+ );
47
+ }
48
+ if (criteria.min_volume !== undefined) {
49
+ quotes = quotes.filter((q) => (q.regularMarketVolume ?? 0) >= criteria.min_volume!);
50
+ }
51
+ if (criteria.min_price !== undefined) {
52
+ quotes = quotes.filter((q) => (q.regularMarketPrice ?? 0) >= criteria.min_price!);
53
+ }
54
+ if (criteria.max_price !== undefined) {
55
+ quotes = quotes.filter((q) => (q.regularMarketPrice ?? Infinity) <= criteria.max_price!);
56
+ }
57
+
58
+ // Sort by market cap descending
59
+ quotes.sort((a, b) => (b.marketCap ?? 0) - (a.marketCap ?? 0));
60
+
61
+ // Limit results
62
+ const limit = criteria.limit ?? 25;
63
+ const total = quotes.length;
64
+ quotes = quotes.slice(0, limit);
65
+
66
+ return formatScreenResults(quotes, criteria, total);
67
+ }
68
+
69
+ function formatScreenResults(
70
+ quotes: YahooQuote[],
71
+ criteria: ScreenCriteria,
72
+ totalMatches: number,
73
+ ): string {
74
+ const lines: string[] = [];
75
+
76
+ lines.push("# Stock Screener Results");
77
+ lines.push("");
78
+
79
+ // Filters applied
80
+ const filters: string[] = [];
81
+ if (criteria.min_market_cap) filters.push(`Min Mkt Cap: ${formatCurrency(criteria.min_market_cap)}`);
82
+ if (criteria.max_market_cap) filters.push(`Max Mkt Cap: ${formatCurrency(criteria.max_market_cap)}`);
83
+ if (criteria.min_pe) filters.push(`Min P/E: ${criteria.min_pe}`);
84
+ if (criteria.max_pe) filters.push(`Max P/E: ${criteria.max_pe}`);
85
+ if (criteria.sector) filters.push(`Sector: ${criteria.sector}`);
86
+ if (criteria.min_volume) filters.push(`Min Volume: ${formatVolume(criteria.min_volume)}`);
87
+ if (criteria.min_price) filters.push(`Min Price: $${criteria.min_price}`);
88
+ if (criteria.max_price) filters.push(`Max Price: $${criteria.max_price}`);
89
+
90
+ if (filters.length > 0) {
91
+ lines.push(`**Filters:** ${filters.join(" | ")}`);
92
+ } else {
93
+ lines.push("**Filters:** None (showing full universe)");
94
+ }
95
+ lines.push(`**Matches:** ${totalMatches} stocks (showing top ${quotes.length})`);
96
+ lines.push("");
97
+
98
+ if (quotes.length === 0) {
99
+ lines.push("_No stocks match the specified criteria. Try broadening your filters._");
100
+ return lines.join("\n");
101
+ }
102
+
103
+ // Results table
104
+ lines.push("| Symbol | Name | Price | Chg% | Volume | Mkt Cap | P/E | Sector |");
105
+ lines.push("|--------|------|-------|------|--------|---------|-----|--------|");
106
+
107
+ for (const q of quotes) {
108
+ const price = q.regularMarketPrice?.toFixed(2) ?? "N/A";
109
+ const changePct = q.regularMarketChangePercent != null
110
+ ? `${q.regularMarketChangePercent >= 0 ? "+" : ""}${q.regularMarketChangePercent.toFixed(2)}%`
111
+ : "N/A";
112
+ const vol = q.regularMarketVolume ? formatVolume(q.regularMarketVolume) : "N/A";
113
+ const cap = q.marketCap ? formatCurrency(q.marketCap) : "N/A";
114
+ const pe = q.trailingPE?.toFixed(1) ?? "N/A";
115
+ const name = (q.shortName ?? q.longName ?? q.symbol).slice(0, 25);
116
+ const sector = q.sector ?? "--";
117
+
118
+ lines.push(`| ${q.symbol} | ${name} | $${price} | ${changePct} | ${vol} | ${cap} | ${pe} | ${sector} |`);
119
+ }
120
+
121
+ lines.push("");
122
+
123
+ // Quick stats
124
+ const withPE = quotes.filter((q) => q.trailingPE != null);
125
+ if (withPE.length > 0) {
126
+ const avgPE = withPE.reduce((s, q) => s + q.trailingPE!, 0) / withPE.length;
127
+ const minPE = Math.min(...withPE.map((q) => q.trailingPE!));
128
+ const maxPE = Math.max(...withPE.map((q) => q.trailingPE!));
129
+ lines.push("## P/E Statistics");
130
+ lines.push("");
131
+ lines.push(`- Average P/E: ${avgPE.toFixed(1)}`);
132
+ lines.push(`- P/E Range: ${minPE.toFixed(1)} - ${maxPE.toFixed(1)}`);
133
+ lines.push("");
134
+ }
135
+
136
+ // Top gainers/losers in results
137
+ const sorted = [...quotes].filter((q) => q.regularMarketChangePercent != null);
138
+ if (sorted.length >= 3) {
139
+ sorted.sort((a, b) => (b.regularMarketChangePercent ?? 0) - (a.regularMarketChangePercent ?? 0));
140
+ const top3 = sorted.slice(0, 3);
141
+ const bottom3 = sorted.slice(-3).reverse();
142
+
143
+ lines.push("## Notable Movers (within results)");
144
+ lines.push("");
145
+ lines.push("**Top gainers:** " + top3.map((q) =>
146
+ `${q.symbol} (${q.regularMarketChangePercent! >= 0 ? "+" : ""}${q.regularMarketChangePercent!.toFixed(2)}%)`
147
+ ).join(", "));
148
+ lines.push("**Top losers:** " + bottom3.map((q) =>
149
+ `${q.symbol} (${q.regularMarketChangePercent!.toFixed(2)}%)`
150
+ ).join(", "));
151
+ }
152
+
153
+ return lines.join("\n");
154
+ }
package/src/types.ts ADDED
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Shared types for the Market Data Analyzer MCP server.
3
+ */
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Yahoo Finance response shapes (partial -- only what we use)
7
+ // ---------------------------------------------------------------------------
8
+
9
+ export interface YahooQuote {
10
+ symbol: string;
11
+ shortName?: string;
12
+ longName?: string;
13
+ regularMarketPrice?: number;
14
+ regularMarketChange?: number;
15
+ regularMarketChangePercent?: number;
16
+ regularMarketVolume?: number;
17
+ regularMarketDayHigh?: number;
18
+ regularMarketDayLow?: number;
19
+ regularMarketOpen?: number;
20
+ regularMarketPreviousClose?: number;
21
+ fiftyTwoWeekHigh?: number;
22
+ fiftyTwoWeekLow?: number;
23
+ fiftyDayAverage?: number;
24
+ twoHundredDayAverage?: number;
25
+ marketCap?: number;
26
+ trailingPE?: number;
27
+ forwardPE?: number;
28
+ priceToBook?: number;
29
+ trailingAnnualDividendYield?: number;
30
+ sector?: string;
31
+ industry?: string;
32
+ averageDailyVolume3Month?: number;
33
+ }
34
+
35
+ export interface YahooChartPoint {
36
+ date: number;
37
+ open: number;
38
+ high: number;
39
+ low: number;
40
+ close: number;
41
+ volume: number;
42
+ }
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // CoinGecko response shapes (partial)
46
+ // ---------------------------------------------------------------------------
47
+
48
+ export interface CoinGeckoMarketData {
49
+ id: string;
50
+ symbol: string;
51
+ name: string;
52
+ current_price: number;
53
+ market_cap: number;
54
+ market_cap_rank: number;
55
+ total_volume: number;
56
+ high_24h: number;
57
+ low_24h: number;
58
+ price_change_24h: number;
59
+ price_change_percentage_24h: number;
60
+ price_change_percentage_7d_in_currency?: number;
61
+ price_change_percentage_30d_in_currency?: number;
62
+ market_cap_change_percentage_24h: number;
63
+ circulating_supply: number;
64
+ total_supply: number | null;
65
+ max_supply: number | null;
66
+ ath: number;
67
+ ath_change_percentage: number;
68
+ ath_date: string;
69
+ atl: number;
70
+ atl_change_percentage: number;
71
+ atl_date: string;
72
+ }
73
+
74
+ export interface CoinGeckoGlobalData {
75
+ total_market_cap: Record<string, number>;
76
+ total_volume: Record<string, number>;
77
+ market_cap_percentage: Record<string, number>;
78
+ market_cap_change_percentage_24h_usd: number;
79
+ active_cryptocurrencies: number;
80
+ }
81
+
82
+ // ---------------------------------------------------------------------------
83
+ // Tool output shapes
84
+ // ---------------------------------------------------------------------------
85
+
86
+ export interface StockAnalysis {
87
+ symbol: string;
88
+ name: string;
89
+ price: number;
90
+ change: number;
91
+ changePercent: number;
92
+ volume: number;
93
+ marketCap: number;
94
+ peRatio: number | null;
95
+ high52w: number;
96
+ low52w: number;
97
+ sma20: number | null;
98
+ sma50: number | null;
99
+ sma200: number | null;
100
+ rsi14: number | null;
101
+ macd: { line: number; signal: number; histogram: number } | null;
102
+ supportLevels: number[];
103
+ resistanceLevels: number[];
104
+ priceHistory: YahooChartPoint[];
105
+ }
106
+
107
+ export interface PortfolioPosition {
108
+ symbol: string;
109
+ shares: number;
110
+ avgCost: number;
111
+ currentPrice: number;
112
+ marketValue: number;
113
+ costBasis: number;
114
+ pnl: number;
115
+ pnlPercent: number;
116
+ allocationPercent: number;
117
+ }
118
+
119
+ export interface PortfolioAnalysis {
120
+ totalValue: number;
121
+ totalCost: number;
122
+ totalPnl: number;
123
+ totalPnlPercent: number;
124
+ positions: PortfolioPosition[];
125
+ diversificationScore: number;
126
+ riskMetrics: {
127
+ concentrationRisk: string;
128
+ largestPosition: string;
129
+ largestPositionPct: number;
130
+ sectorBreakdown: Record<string, number>;
131
+ };
132
+ warnings: string[];
133
+ }
134
+
135
+ export interface ScreenerResult {
136
+ symbol: string;
137
+ name: string;
138
+ price: number;
139
+ changePercent: number;
140
+ marketCap: number;
141
+ peRatio: number | null;
142
+ volume: number;
143
+ sector?: string;
144
+ }
145
+
146
+ export interface AssetComparison {
147
+ symbol: string;
148
+ name: string;
149
+ totalReturn: number;
150
+ annualizedReturn: number;
151
+ volatility: number;
152
+ sharpeRatio: number;
153
+ maxDrawdown: number;
154
+ currentPrice: number;
155
+ }
156
+
157
+ export interface CryptoAnalysisResult {
158
+ id: string;
159
+ symbol: string;
160
+ name: string;
161
+ price: number;
162
+ change24h: number;
163
+ changePct24h: number;
164
+ volume24h: number;
165
+ marketCap: number;
166
+ marketCapRank: number;
167
+ circulatingSupply: number;
168
+ totalSupply: number | null;
169
+ maxSupply: number | null;
170
+ ath: number;
171
+ athChangePct: number;
172
+ athDate: string;
173
+ dominance: number | null;
174
+ fearGreedApprox: string;
175
+ }