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 @@
1
+ {"version":3,"file":"math.js","sourceRoot":"","sources":["../../src/utils/math.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "market-data-analyzer",
3
+ "version": "2.0.0",
4
+ "description": "MCP server for financial market data analysis -- live stock analysis, screening, portfolio analytics, crypto analysis, and market overview using free APIs.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "market-data-analyzer": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "start": "node dist/index.js",
13
+ "start:sse": "node dist/index.js --sse",
14
+ "dev": "tsc --watch",
15
+ "prepublishOnly": "npm run build"
16
+ },
17
+ "keywords": ["mcp", "finance", "stocks", "crypto", "technical-analysis", "portfolio", "market-data", "trading", "yahoo-finance", "coingecko"],
18
+ "license": "MIT",
19
+ "dependencies": {
20
+ "@modelcontextprotocol/sdk": "^1.29.0",
21
+ "zod": "^3.25.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^22.0.0",
25
+ "typescript": "^5.7.0"
26
+ },
27
+ "engines": {
28
+ "node": ">=18.0.0"
29
+ }
30
+ }
package/src/index.ts ADDED
@@ -0,0 +1,329 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Market Data Analyzer -- MCP Server
5
+ *
6
+ * Finance-focused MCP server with live data from Yahoo Finance and CoinGecko.
7
+ * Tools: analyze_stock, screen_stocks, analyze_portfolio, compare_assets,
8
+ * market_overview, crypto_analysis.
9
+ *
10
+ * Transports: stdio (default) or SSE (--sse flag).
11
+ */
12
+
13
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
14
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
15
+ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
16
+ import { z } from "zod";
17
+ import http from "node:http";
18
+
19
+ import { handleAnalyzeStock } from "./tools/analyze_stock.js";
20
+ import { handleScreenStocks } from "./tools/screen_stocks.js";
21
+ import { handleAnalyzePortfolio } from "./tools/analyze_portfolio.js";
22
+ import { handleCompareAssets } from "./tools/compare_assets.js";
23
+ import { handleMarketOverview } from "./tools/market_overview.js";
24
+ import { handleCryptoAnalysis } from "./tools/crypto_analysis.js";
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // Server setup
28
+ // ---------------------------------------------------------------------------
29
+
30
+ const server = new McpServer({
31
+ name: "market-data-analyzer",
32
+ version: "2.0.0",
33
+ });
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // Tool: analyze_stock
37
+ // ---------------------------------------------------------------------------
38
+
39
+ server.tool(
40
+ "analyze_stock",
41
+ "Deep analysis of a stock symbol: price, moving averages (SMA 20/50/200), RSI, MACD, support/resistance levels. Uses live Yahoo Finance data.",
42
+ {
43
+ symbol: z
44
+ .string()
45
+ .describe("Stock ticker symbol (e.g. 'AAPL', 'MSFT', 'TSLA')"),
46
+ },
47
+ async ({ symbol }) => {
48
+ try {
49
+ const result = await handleAnalyzeStock(symbol.toUpperCase());
50
+ return { content: [{ type: "text" as const, text: result }] };
51
+ } catch (err) {
52
+ return {
53
+ content: [
54
+ {
55
+ type: "text" as const,
56
+ text: `Error analyzing ${symbol}: ${err instanceof Error ? err.message : String(err)}`,
57
+ },
58
+ ],
59
+ isError: true,
60
+ };
61
+ }
62
+ },
63
+ );
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Tool: screen_stocks
67
+ // ---------------------------------------------------------------------------
68
+
69
+ server.tool(
70
+ "screen_stocks",
71
+ "Screen stocks by criteria: market cap range, P/E ratio, sector, volume threshold. Returns top matches with key metrics. Fetches live data from Yahoo Finance for ~70 popular stocks.",
72
+ {
73
+ min_market_cap: z
74
+ .number()
75
+ .optional()
76
+ .describe("Minimum market cap in USD (e.g. 1000000000 for $1B)"),
77
+ max_market_cap: z.number().optional().describe("Maximum market cap in USD"),
78
+ min_pe: z.number().optional().describe("Minimum trailing P/E ratio"),
79
+ max_pe: z.number().optional().describe("Maximum trailing P/E ratio"),
80
+ sector: z
81
+ .string()
82
+ .optional()
83
+ .describe("Sector filter (partial match, e.g. 'tech', 'health', 'energy')"),
84
+ min_volume: z.number().optional().describe("Minimum daily trading volume"),
85
+ min_price: z.number().optional().describe("Minimum stock price"),
86
+ max_price: z.number().optional().describe("Maximum stock price"),
87
+ limit: z
88
+ .number()
89
+ .int()
90
+ .min(1)
91
+ .max(50)
92
+ .optional()
93
+ .describe("Max results to return (default: 25)"),
94
+ },
95
+ async (criteria) => {
96
+ try {
97
+ const result = await handleScreenStocks(criteria);
98
+ return { content: [{ type: "text" as const, text: result }] };
99
+ } catch (err) {
100
+ return {
101
+ content: [
102
+ {
103
+ type: "text" as const,
104
+ text: `Error screening stocks: ${err instanceof Error ? err.message : String(err)}`,
105
+ },
106
+ ],
107
+ isError: true,
108
+ };
109
+ }
110
+ },
111
+ );
112
+
113
+ // ---------------------------------------------------------------------------
114
+ // Tool: analyze_portfolio
115
+ // ---------------------------------------------------------------------------
116
+
117
+ server.tool(
118
+ "analyze_portfolio",
119
+ "Analyze a portfolio of holdings. Provide positions with symbol, shares, and average cost. Returns total value, P&L per position, allocation %, diversification score, and risk metrics. Fetches live prices from Yahoo Finance.",
120
+ {
121
+ holdings: z
122
+ .array(
123
+ z.object({
124
+ symbol: z.string().describe("Ticker symbol (e.g. 'AAPL')"),
125
+ shares: z.number().describe("Number of shares held"),
126
+ avg_cost: z.number().describe("Average cost per share"),
127
+ }),
128
+ )
129
+ .min(1)
130
+ .describe("Array of portfolio holdings"),
131
+ },
132
+ async ({ holdings }) => {
133
+ try {
134
+ const result = await handleAnalyzePortfolio(holdings);
135
+ return { content: [{ type: "text" as const, text: result }] };
136
+ } catch (err) {
137
+ return {
138
+ content: [
139
+ {
140
+ type: "text" as const,
141
+ text: `Error analyzing portfolio: ${err instanceof Error ? err.message : String(err)}`,
142
+ },
143
+ ],
144
+ isError: true,
145
+ };
146
+ }
147
+ },
148
+ );
149
+
150
+ // ---------------------------------------------------------------------------
151
+ // Tool: compare_assets
152
+ // ---------------------------------------------------------------------------
153
+
154
+ server.tool(
155
+ "compare_assets",
156
+ "Compare 2-5 assets side by side: returns, volatility, correlation, Sharpe ratio approximation over a given period. Uses Yahoo Finance historical data.",
157
+ {
158
+ symbols: z
159
+ .array(z.string())
160
+ .min(2)
161
+ .max(5)
162
+ .describe("Array of 2-5 ticker symbols to compare (e.g. ['AAPL', 'MSFT', 'GOOGL'])"),
163
+ period: z
164
+ .enum(["1mo", "3mo", "6mo", "1y", "2y", "5y"])
165
+ .optional()
166
+ .describe("Comparison period (default: '6mo')"),
167
+ },
168
+ async ({ symbols, period }) => {
169
+ try {
170
+ const result = await handleCompareAssets(
171
+ symbols.map((s) => s.toUpperCase()),
172
+ period ?? "6mo",
173
+ );
174
+ return { content: [{ type: "text" as const, text: result }] };
175
+ } catch (err) {
176
+ return {
177
+ content: [
178
+ {
179
+ type: "text" as const,
180
+ text: `Error comparing assets: ${err instanceof Error ? err.message : String(err)}`,
181
+ },
182
+ ],
183
+ isError: true,
184
+ };
185
+ }
186
+ },
187
+ );
188
+
189
+ // ---------------------------------------------------------------------------
190
+ // Tool: market_overview
191
+ // ---------------------------------------------------------------------------
192
+
193
+ server.tool(
194
+ "market_overview",
195
+ "Current market snapshot: major indices (S&P 500, NASDAQ, DOW), sector performance via ETFs, market breadth indicators, VIX, crypto, and commodities. Live data from Yahoo Finance.",
196
+ {},
197
+ async () => {
198
+ try {
199
+ const result = await handleMarketOverview();
200
+ return { content: [{ type: "text" as const, text: result }] };
201
+ } catch (err) {
202
+ return {
203
+ content: [
204
+ {
205
+ type: "text" as const,
206
+ text: `Error fetching market overview: ${err instanceof Error ? err.message : String(err)}`,
207
+ },
208
+ ],
209
+ isError: true,
210
+ };
211
+ }
212
+ },
213
+ );
214
+
215
+ // ---------------------------------------------------------------------------
216
+ // Tool: crypto_analysis
217
+ // ---------------------------------------------------------------------------
218
+
219
+ server.tool(
220
+ "crypto_analysis",
221
+ "Crypto-specific analysis: price, 24h volume, market dominance, fear/greed index approximation, supply metrics, ATH/ATL data. Uses CoinGecko free API.",
222
+ {
223
+ symbol: z
224
+ .string()
225
+ .describe("Cryptocurrency symbol or name (e.g. 'BTC', 'ETH', 'SOL', 'bitcoin')"),
226
+ },
227
+ async ({ symbol }) => {
228
+ try {
229
+ const result = await handleCryptoAnalysis(symbol);
230
+ return { content: [{ type: "text" as const, text: result }] };
231
+ } catch (err) {
232
+ return {
233
+ content: [
234
+ {
235
+ type: "text" as const,
236
+ text: `Error analyzing crypto ${symbol}: ${err instanceof Error ? err.message : String(err)}`,
237
+ },
238
+ ],
239
+ isError: true,
240
+ };
241
+ }
242
+ },
243
+ );
244
+
245
+ // ---------------------------------------------------------------------------
246
+ // Transport: stdio or SSE
247
+ // ---------------------------------------------------------------------------
248
+
249
+ async function main(): Promise<void> {
250
+ const useSSE = process.argv.includes("--sse");
251
+
252
+ if (useSSE) {
253
+ const port = parseInt(process.env.PORT ?? "3000", 10);
254
+ const transports = new Map<string, SSEServerTransport>();
255
+
256
+ const httpServer = http.createServer(async (req, res) => {
257
+ // CORS headers
258
+ res.setHeader("Access-Control-Allow-Origin", "*");
259
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
260
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type");
261
+
262
+ if (req.method === "OPTIONS") {
263
+ res.writeHead(204);
264
+ res.end();
265
+ return;
266
+ }
267
+
268
+ const url = new URL(req.url ?? "/", `http://localhost:${port}`);
269
+
270
+ if (url.pathname === "/sse" && req.method === "GET") {
271
+ const transport = new SSEServerTransport("/messages", res);
272
+ const sessionId = transport.sessionId;
273
+ transports.set(sessionId, transport);
274
+
275
+ res.on("close", () => {
276
+ transports.delete(sessionId);
277
+ });
278
+
279
+ await server.connect(transport);
280
+ return;
281
+ }
282
+
283
+ if (url.pathname === "/messages" && req.method === "POST") {
284
+ const sessionId = url.searchParams.get("sessionId");
285
+ if (!sessionId || !transports.has(sessionId)) {
286
+ res.writeHead(400, { "Content-Type": "application/json" });
287
+ res.end(JSON.stringify({ error: "Invalid or missing sessionId" }));
288
+ return;
289
+ }
290
+ const transport = transports.get(sessionId)!;
291
+ await transport.handlePostMessage(req, res);
292
+ return;
293
+ }
294
+
295
+ if (url.pathname === "/health") {
296
+ res.writeHead(200, { "Content-Type": "application/json" });
297
+ res.end(
298
+ JSON.stringify({
299
+ status: "ok",
300
+ server: "market-data-analyzer",
301
+ version: "2.0.0",
302
+ }),
303
+ );
304
+ return;
305
+ }
306
+
307
+ res.writeHead(404, { "Content-Type": "application/json" });
308
+ res.end(JSON.stringify({ error: "Not found" }));
309
+ });
310
+
311
+ httpServer.listen(port, () => {
312
+ console.error(
313
+ `Market Data Analyzer MCP server (SSE) listening on port ${port}`,
314
+ );
315
+ console.error(` SSE endpoint: http://localhost:${port}/sse`);
316
+ console.error(` Messages endpoint: http://localhost:${port}/messages`);
317
+ console.error(` Health check: http://localhost:${port}/health`);
318
+ });
319
+ } else {
320
+ const transport = new StdioServerTransport();
321
+ await server.connect(transport);
322
+ console.error("Market Data Analyzer MCP server running on stdio");
323
+ }
324
+ }
325
+
326
+ main().catch((err) => {
327
+ console.error("Fatal error:", err);
328
+ process.exit(1);
329
+ });
@@ -0,0 +1,204 @@
1
+ /**
2
+ * analyze_portfolio -- Portfolio analytics with live prices.
3
+ *
4
+ * Takes an array of { symbol, shares, avg_cost } holdings, fetches
5
+ * current prices from Yahoo Finance, and returns P&L, allocation,
6
+ * diversification score, and risk metrics.
7
+ */
8
+
9
+ import { yahooQuote } from "../utils/api.js";
10
+ import { formatCurrency } from "../utils/math.js";
11
+
12
+ export interface PortfolioHolding {
13
+ symbol: string;
14
+ shares: number;
15
+ avg_cost: number;
16
+ }
17
+
18
+ interface PositionResult {
19
+ symbol: string;
20
+ name: string;
21
+ shares: number;
22
+ avgCost: number;
23
+ currentPrice: number;
24
+ marketValue: number;
25
+ costBasis: number;
26
+ pnl: number;
27
+ pnlPercent: number;
28
+ allocationPercent: number;
29
+ sector: string;
30
+ }
31
+
32
+ export async function handleAnalyzePortfolio(
33
+ holdings: PortfolioHolding[],
34
+ ): Promise<string> {
35
+ if (holdings.length === 0) {
36
+ throw new Error("At least one holding is required.");
37
+ }
38
+
39
+ // Fetch live quotes for all symbols
40
+ const symbols = holdings.map((h) => h.symbol);
41
+ const quotes = await yahooQuote(symbols);
42
+ const quoteMap = new Map(quotes.map((q) => [q.symbol, q]));
43
+
44
+ // Build position analysis
45
+ let totalValue = 0;
46
+ let totalCost = 0;
47
+
48
+ const positions: PositionResult[] = holdings.map((h) => {
49
+ const q = quoteMap.get(h.symbol);
50
+ const currentPrice = q?.regularMarketPrice ?? 0;
51
+ const marketValue = h.shares * currentPrice;
52
+ const costBasis = h.shares * h.avg_cost;
53
+ totalValue += marketValue;
54
+ totalCost += costBasis;
55
+
56
+ return {
57
+ symbol: h.symbol,
58
+ name: q?.shortName ?? q?.longName ?? h.symbol,
59
+ shares: h.shares,
60
+ avgCost: h.avg_cost,
61
+ currentPrice,
62
+ marketValue,
63
+ costBasis,
64
+ pnl: marketValue - costBasis,
65
+ pnlPercent: costBasis > 0 ? ((marketValue - costBasis) / costBasis) * 100 : 0,
66
+ allocationPercent: 0, // calculated below
67
+ sector: q?.sector ?? "Unknown",
68
+ };
69
+ });
70
+
71
+ // Calculate allocation percentages
72
+ for (const p of positions) {
73
+ p.allocationPercent = totalValue > 0 ? (p.marketValue / totalValue) * 100 : 0;
74
+ }
75
+
76
+ // Sort by market value descending
77
+ positions.sort((a, b) => b.marketValue - a.marketValue);
78
+
79
+ // Sector breakdown
80
+ const sectorAlloc: Record<string, number> = {};
81
+ for (const p of positions) {
82
+ sectorAlloc[p.sector] = (sectorAlloc[p.sector] ?? 0) + p.allocationPercent;
83
+ }
84
+
85
+ // Diversification score (HHI-based, 0-100 where 100 = perfectly diversified)
86
+ const weights = positions.map((p) => p.allocationPercent / 100);
87
+ const hhi = weights.reduce((s, w) => s + w * w, 0);
88
+ const n = weights.length;
89
+ const minHHI = n > 0 ? 1 / n : 1;
90
+ const divScore = n <= 1 ? 0 : Math.round((1 - (hhi - minHHI) / (1 - minHHI)) * 100);
91
+
92
+ // Risk warnings
93
+ const warnings: string[] = [];
94
+
95
+ for (const p of positions) {
96
+ if (p.allocationPercent > 30) {
97
+ warnings.push(`HIGH CONCENTRATION: ${p.symbol} is ${p.allocationPercent.toFixed(1)}% of portfolio.`);
98
+ }
99
+ }
100
+
101
+ for (const [sector, pct] of Object.entries(sectorAlloc)) {
102
+ if (pct > 50) {
103
+ warnings.push(`SECTOR RISK: ${sector} represents ${pct.toFixed(1)}% of portfolio.`);
104
+ }
105
+ }
106
+
107
+ for (const p of positions) {
108
+ if (p.pnlPercent < -20) {
109
+ warnings.push(`SIGNIFICANT LOSS: ${p.symbol} is down ${Math.abs(p.pnlPercent).toFixed(1)}%.`);
110
+ }
111
+ }
112
+
113
+ if (n < 5) {
114
+ warnings.push(`LOW DIVERSIFICATION: Only ${n} position(s). Consider adding more holdings.`);
115
+ }
116
+
117
+ if (Object.keys(sectorAlloc).length < 3 && n >= 3) {
118
+ warnings.push("LIMITED SECTOR EXPOSURE: Portfolio spans fewer than 3 sectors.");
119
+ }
120
+
121
+ // Any positions where we couldn't fetch a price?
122
+ const missingPrices = positions.filter((p) => p.currentPrice === 0);
123
+ for (const p of missingPrices) {
124
+ warnings.push(`MISSING DATA: Could not fetch price for ${p.symbol}. Values may be inaccurate.`);
125
+ }
126
+
127
+ // Format output
128
+ const lines: string[] = [];
129
+
130
+ lines.push("# Portfolio Analysis");
131
+ lines.push("");
132
+
133
+ // Summary
134
+ lines.push("## Summary");
135
+ lines.push("");
136
+ lines.push("| Metric | Value |");
137
+ lines.push("|--------|-------|");
138
+ lines.push(`| Total Value | ${formatCurrency(totalValue)} |`);
139
+ lines.push(`| Total Cost Basis | ${formatCurrency(totalCost)} |`);
140
+ const totalPnl = totalValue - totalCost;
141
+ const totalPnlPct = totalCost > 0 ? ((totalPnl) / totalCost) * 100 : 0;
142
+ lines.push(`| Total P&L | ${totalPnl >= 0 ? "+" : ""}${formatCurrency(totalPnl)} (${totalPnlPct >= 0 ? "+" : ""}${totalPnlPct.toFixed(2)}%) |`);
143
+ lines.push(`| Positions | ${n} |`);
144
+ lines.push(`| Sectors | ${Object.keys(sectorAlloc).length} |`);
145
+ lines.push(`| Diversification Score | ${divScore}/100 |`);
146
+ lines.push("");
147
+
148
+ // Positions
149
+ lines.push("## Positions");
150
+ lines.push("");
151
+ lines.push("| Symbol | Name | Shares | Avg Cost | Price | Value | P&L | P&L% | Alloc% |");
152
+ lines.push("|--------|------|--------|----------|-------|-------|-----|------|--------|");
153
+
154
+ for (const p of positions) {
155
+ const pnlSign = p.pnl >= 0 ? "+" : "";
156
+ lines.push(
157
+ `| ${p.symbol} | ${p.name.slice(0, 20)} | ${p.shares} | $${p.avgCost.toFixed(2)} | $${p.currentPrice.toFixed(2)} | ${formatCurrency(p.marketValue)} | ${pnlSign}${formatCurrency(p.pnl)} | ${pnlSign}${p.pnlPercent.toFixed(2)}% | ${p.allocationPercent.toFixed(1)}% |`,
158
+ );
159
+ }
160
+ lines.push("");
161
+
162
+ // Sector allocation
163
+ lines.push("## Sector Allocation");
164
+ lines.push("");
165
+ lines.push("| Sector | Allocation |");
166
+ lines.push("|--------|------------|");
167
+ const sortedSectors = Object.entries(sectorAlloc).sort((a, b) => b[1] - a[1]);
168
+ for (const [sector, pct] of sortedSectors) {
169
+ const bar = "=".repeat(Math.round(pct / 2));
170
+ lines.push(`| ${sector} | ${pct.toFixed(1)}% ${bar} |`);
171
+ }
172
+ lines.push("");
173
+
174
+ // Winners and losers
175
+ const winners = [...positions].sort((a, b) => b.pnlPercent - a.pnlPercent);
176
+ if (winners.length > 0) {
177
+ lines.push("## Top Performers");
178
+ lines.push("");
179
+ const top3 = winners.slice(0, Math.min(3, winners.length));
180
+ for (const p of top3) {
181
+ lines.push(`- **${p.symbol}**: ${p.pnlPercent >= 0 ? "+" : ""}${p.pnlPercent.toFixed(2)}% (${p.pnl >= 0 ? "+" : ""}${formatCurrency(p.pnl)})`);
182
+ }
183
+ lines.push("");
184
+ const bottom3 = winners.slice(-Math.min(3, winners.length)).reverse();
185
+ lines.push("## Underperformers");
186
+ lines.push("");
187
+ for (const p of bottom3) {
188
+ lines.push(`- **${p.symbol}**: ${p.pnlPercent >= 0 ? "+" : ""}${p.pnlPercent.toFixed(2)}% (${p.pnl >= 0 ? "+" : ""}${formatCurrency(p.pnl)})`);
189
+ }
190
+ lines.push("");
191
+ }
192
+
193
+ // Risk warnings
194
+ if (warnings.length > 0) {
195
+ lines.push("## Risk Warnings");
196
+ lines.push("");
197
+ for (const w of warnings) {
198
+ lines.push(`- ${w}`);
199
+ }
200
+ lines.push("");
201
+ }
202
+
203
+ return lines.join("\n");
204
+ }