avanza-mcp 1.1.0__py3-none-any.whl → 1.3.0__py3-none-any.whl

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,503 @@
1
+ """Usage guide resources for AI callers."""
2
+
3
+ from .. import mcp
4
+
5
+ USAGE_GUIDE = """# Avanza MCP Server - Usage Guide for AI Assistants
6
+
7
+ ## 🎯 Critical: When NOT to Use MCP Tools
8
+
9
+ ### MCP Tools Are For Interactive Exploration, NOT Bulk Data Processing
10
+
11
+ **Use MCP tools for**:
12
+ - ✅ Single instrument lookups (1-5 items)
13
+ - ✅ Small comparisons (2-10 instruments)
14
+ - ✅ Quick searches and exploration
15
+ - ✅ Interactive data analysis
16
+
17
+ **DO NOT use MCP tools for**:
18
+ - ❌ Fetching data for 50+ instruments
19
+ - ❌ Bulk screening operations
20
+ - ❌ Large-scale data analysis
21
+ - ❌ Building datasets
22
+
23
+ ## 📏 The Threshold Rule
24
+
25
+ | Number of Items | Action |
26
+ |----------------|---------|
27
+ | 1-20 items | Use MCP tools directly |
28
+ | 20-50 items | Ask user if they want to continue with tools OR get a script |
29
+ | 50+ items | **DO NOT USE MCP TOOLS** - Provide a script instead |
30
+
31
+ ## 🚫 Common Anti-Pattern (DON'T DO THIS)
32
+
33
+ ```
34
+ User: "Analyze the top 100 Swedish stocks"
35
+
36
+ ❌ BAD Response:
37
+ for stock_id in top_100_stocks:
38
+ get_stock_info(stock_id) # This will make 100 MCP calls!
39
+ get_stock_chart(stock_id) # Another 100 calls!
40
+ ```
41
+
42
+ **Problem**: This creates 200+ MCP tool calls, which is:
43
+ - Slow and inefficient
44
+ - Overloads the MCP connection
45
+ - Not what MCP tools are designed for
46
+
47
+ ## ✅ Correct Approach for Large Data Fetching
48
+
49
+ ### Option 1: Provide a Python Script
50
+
51
+ ```python
52
+ User: "Analyze the top 100 Swedish stocks"
53
+
54
+ ✅ GOOD Response:
55
+ "For analyzing 100 stocks, you should fetch the data directly using a script
56
+ rather than MCP tools. Here's a Python script you can run:
57
+
58
+ ```python
59
+ import httpx
60
+ import asyncio
61
+ import json
62
+
63
+ async def fetch_stock_info(client, stock_id):
64
+ url = f"https://www.avanza.se/_api/market-guide/stock/{stock_id}"
65
+ response = await client.get(url)
66
+ return response.json()
67
+
68
+ async def main():
69
+ stock_ids = ["5247", "5361", ...] # Your 100 stock IDs
70
+
71
+ async with httpx.AsyncClient() as client:
72
+ tasks = [fetch_stock_info(client, sid) for sid in stock_ids]
73
+ results = await asyncio.gather(*tasks)
74
+
75
+ # Save to file
76
+ with open('stocks_data.json', 'w') as f:
77
+ json.dump(results, f, indent=2)
78
+
79
+ print(f"Fetched {len(results)} stocks")
80
+
81
+ asyncio.run(main())
82
+ ```
83
+
84
+ This will efficiently fetch all 100 stocks in parallel and save to a JSON file."
85
+ ```
86
+
87
+ ### Option 2: Provide curl Commands
88
+
89
+ ```bash
90
+ User: "Get data for 50 certificates"
91
+
92
+ ✅ GOOD Response:
93
+ "For fetching 50 certificates, use the filter endpoint directly with a script:
94
+
95
+ ```bash
96
+ # Get all certificates at once with pagination
97
+ curl 'https://www.avanza.se/_api/market-certificate-filter/' \\
98
+ -H 'Content-Type: application/json' \\
99
+ --data-raw '{
100
+ "filter": {},
101
+ "offset": 0,
102
+ "limit": 50,
103
+ "sortBy": {"field": "name", "order": "asc"}
104
+ }' > certificates.json
105
+
106
+ # For more than 50, fetch next page:
107
+ curl 'https://www.avanza.se/_api/market-certificate-filter/' \\
108
+ -H 'Content-Type: application/json' \\
109
+ --data-raw '{
110
+ "filter": {},
111
+ "offset": 50,
112
+ "limit": 50,
113
+ "sortBy": {"field": "name", "order": "asc"}
114
+ }' >> certificates.json
115
+ ```
116
+
117
+ This fetches the data directly from Avanza's public API."
118
+ ```
119
+
120
+ ### Option 3: Provide a Shell Script for Multiple Items
121
+
122
+ ```bash
123
+ User: "Get stock info for these 100 companies"
124
+
125
+ ✅ GOOD Response:
126
+ "Here's a shell script to fetch data for all 100 stocks:
127
+
128
+ ```bash
129
+ #!/bin/bash
130
+
131
+ # Array of stock IDs
132
+ stock_ids=(5247 5361 5239 ...) # All 100 IDs
133
+
134
+ # Create output directory
135
+ mkdir -p stock_data
136
+
137
+ # Fetch each stock
138
+ for id in "${stock_ids[@]}"; do
139
+ echo "Fetching stock $id..."
140
+ curl -s "https://www.avanza.se/_api/market-guide/stock/$id" \\
141
+ > "stock_data/stock_$id.json"
142
+ sleep 0.5 # Be nice to the API
143
+ done
144
+
145
+ echo "Done! Data saved in stock_data/"
146
+ ```
147
+
148
+ Run with: `chmod +x fetch_stocks.sh && ./fetch_stocks.sh`"
149
+ ```
150
+
151
+ ## 📋 API Endpoint Reference (For Script Writing)
152
+
153
+ When instructing users to write scripts, these are the public endpoints:
154
+
155
+ ### Search & Discovery
156
+ ```bash
157
+ # Search instruments
158
+ curl 'https://www.avanza.se/_api/search/filtered-search' \\
159
+ -H 'Content-Type: application/json' \\
160
+ --data-raw '{"query": "Tesla", "instrumentTypes": ["STOCK"]}'
161
+ ```
162
+
163
+ ### Stock Data
164
+ ```bash
165
+ # Stock info
166
+ curl 'https://www.avanza.se/_api/market-guide/stock/{id}'
167
+
168
+ # Stock quote
169
+ curl 'https://www.avanza.se/_api/market-guide/stock/{id}/quote'
170
+
171
+ # Stock chart
172
+ curl 'https://www.avanza.se/_api/price-chart/stock/{id}?timePeriod=one_month'
173
+
174
+ # Order book
175
+ curl 'https://www.avanza.se/_api/market-guide/stock/{id}/orderdepth'
176
+ ```
177
+
178
+ ### Filter Endpoints (Bulk Operations)
179
+ ```bash
180
+ # Filter certificates (POST request)
181
+ curl 'https://www.avanza.se/_api/market-certificate-filter/' \\
182
+ -H 'Content-Type: application/json' \\
183
+ --data-raw '{
184
+ "filter": {"directions": ["long"]},
185
+ "offset": 0,
186
+ "limit": 100,
187
+ "sortBy": {"field": "name", "order": "asc"}
188
+ }'
189
+
190
+ # Filter ETFs (POST request)
191
+ curl 'https://www.avanza.se/_api/market-etf-filter/' \\
192
+ -H 'Content-Type: application/json' \\
193
+ --data-raw '{
194
+ "filter": {"exposures": ["usa"]},
195
+ "offset": 0,
196
+ "limit": 100,
197
+ "sortBy": {"field": "managementFee", "order": "asc"}
198
+ }'
199
+
200
+ # Filter warrants (POST request)
201
+ curl 'https://www.avanza.se/_api/market-warrant-filter/' \\
202
+ -H 'Content-Type: application/json' \\
203
+ --data-raw '{
204
+ "filter": {"subTypes": ["TURBO"]},
205
+ "offset": 0,
206
+ "limit": 100,
207
+ "sortBy": {"field": "name", "order": "asc"}
208
+ }'
209
+ ```
210
+
211
+ ### Fund Data
212
+ ```bash
213
+ # Fund info
214
+ curl 'https://www.avanza.se/_api/fund-guide/guide/{id}'
215
+
216
+ # Fund chart
217
+ curl 'https://www.avanza.se/_api/fund-guide/chart/{id}/three_years'
218
+ ```
219
+
220
+ ## 🔄 Decision Tree: Tool vs Script
221
+
222
+ ```
223
+ User request → Count number of items needed
224
+
225
+ ├─ 1-20 items? → Use MCP tools directly
226
+
227
+ ├─ 20-50 items? → Ask user:
228
+ │ "This will require ~{count} API calls. Would you like me to:
229
+ │ 1. Proceed with MCP tools (slower, interactive)
230
+ │ 2. Provide a script for you to run (faster, bulk)"
231
+
232
+ └─ 50+ items? → ALWAYS provide script
233
+
234
+ ├─ User is technical? → Python script with async
235
+ ├─ User wants simple? → Bash script with curl
236
+ └─ User wants data file? → curl command + jq
237
+ ```
238
+
239
+ ## 🎓 Examples of Correct Responses
240
+
241
+ ### Example 1: Small Request (Use Tools)
242
+
243
+ ```
244
+ User: "What's the current price of Volvo?"
245
+
246
+ ✅ Response:
247
+ search_instruments(query="Volvo B", instrument_types=["STOCK"])
248
+ get_stock_quote(instrument_id="5247")
249
+ "Volvo B is currently trading at 265.40 SEK, up 1.2%"
250
+ ```
251
+
252
+ ### Example 2: Medium Request (Ask User)
253
+
254
+ ```
255
+ User: "Compare these 30 ETFs"
256
+
257
+ ✅ Response:
258
+ "Comparing 30 ETFs will require approximately 60 API calls through MCP tools.
259
+ I have two options for you:
260
+
261
+ 1. **Use MCP Tools** (slower, ~2-3 minutes):
262
+ I'll fetch each ETF's data interactively. Good if you want to explore
263
+ the data step-by-step.
264
+
265
+ 2. **Provide a Script** (faster, ~30 seconds):
266
+ I'll give you a Python script that fetches all 30 ETFs in parallel,
267
+ saves to JSON, and you can analyze the results.
268
+
269
+ Which would you prefer?"
270
+ ```
271
+
272
+ ### Example 3: Large Request (Provide Script)
273
+
274
+ ```
275
+ User: "Analyze all Swedish stocks with market cap > 1B SEK"
276
+
277
+ ✅ Response:
278
+ "To analyze 100+ stocks efficiently, you should fetch the data directly
279
+ rather than using MCP tools. Here's a Python script:
280
+
281
+ ```python
282
+ import httpx
283
+ import asyncio
284
+ import pandas as pd
285
+
286
+ async def fetch_stock(client, stock_id):
287
+ try:
288
+ response = await client.get(
289
+ f"https://www.avanza.se/_api/market-guide/stock/{stock_id}"
290
+ )
291
+ return response.json()
292
+ except Exception as e:
293
+ print(f"Error fetching {stock_id}: {e}")
294
+ return None
295
+
296
+ async def main():
297
+ # First, get Swedish stock IDs (you'd need to search or have a list)
298
+ stock_ids = [...] # Your list of stock IDs
299
+
300
+ async with httpx.AsyncClient(timeout=30.0) as client:
301
+ tasks = [fetch_stock(client, sid) for sid in stock_ids]
302
+ results = await asyncio.gather(*tasks)
303
+
304
+ # Filter for market cap > 1B
305
+ large_caps = [
306
+ s for s in results
307
+ if s and s.get('company', {}).get('marketCapital', 0) > 1_000_000_000
308
+ ]
309
+
310
+ # Convert to DataFrame for analysis
311
+ df = pd.DataFrame([{
312
+ 'name': s['name'],
313
+ 'price': s['quote']['last'],
314
+ 'market_cap': s['company']['marketCapital'],
315
+ 'pe_ratio': s.get('keyIndicators', {}).get('priceEarningsRatio'),
316
+ } for s in large_caps])
317
+
318
+ df.to_csv('large_cap_stocks.csv', index=False)
319
+ print(f"Analyzed {len(large_caps)} large-cap stocks")
320
+ print(df.head())
321
+
322
+ asyncio.run(main())
323
+ ```
324
+
325
+ Save this as `analyze_stocks.py` and run with:
326
+ ```bash
327
+ pip install httpx pandas
328
+ python analyze_stocks.py
329
+ ```
330
+
331
+ After running this, I can help you analyze the results in `large_cap_stocks.csv`."
332
+ ```
333
+
334
+ ## 💡 Key Principles
335
+
336
+ 1. **MCP tools = Interactive exploration** (1-20 items)
337
+ 2. **Scripts = Bulk data processing** (50+ items)
338
+ 3. **Always explain why** you're recommending a script over tools
339
+ 4. **Provide working code** that users can run immediately
340
+ 5. **Include error handling** in scripts (timeouts, retries)
341
+ 6. **Be nice to the API** (rate limiting, delays)
342
+
343
+ ## 🚀 Script Templates You Can Use
344
+
345
+ ### Python: Async Bulk Fetcher
346
+ ```python
347
+ import httpx
348
+ import asyncio
349
+
350
+ async def fetch_data(url):
351
+ async with httpx.AsyncClient() as client:
352
+ response = await client.get(url, timeout=30.0)
353
+ return response.json()
354
+
355
+ async def bulk_fetch(urls):
356
+ tasks = [fetch_data(url) for url in urls]
357
+ return await asyncio.gather(*tasks, return_exceptions=True)
358
+
359
+ # Usage
360
+ urls = [f"https://www.avanza.se/_api/market-guide/stock/{i}" for i in stock_ids]
361
+ results = asyncio.run(bulk_fetch(urls))
362
+ ```
363
+
364
+ ### Bash: Simple Sequential Fetcher
365
+ ```bash
366
+ #!/bin/bash
367
+ for id in $(cat stock_ids.txt); do
368
+ curl -s "https://www.avanza.se/_api/market-guide/stock/$id" \\
369
+ > "data/stock_$id.json"
370
+ sleep 0.5
371
+ done
372
+ ```
373
+
374
+ ### curl + jq: Filter and Extract
375
+ ```bash
376
+ # Fetch and extract just the fields you need
377
+ curl -s 'https://www.avanza.se/_api/market-certificate-filter/' \\
378
+ -H 'Content-Type: application/json' \\
379
+ --data-raw '{"filter":{},"offset":0,"limit":100,"sortBy":{"field":"name","order":"asc"}}' \\
380
+ | jq '.certificates[] | {name, orderbookId, leverage, direction}'
381
+ ```
382
+
383
+ ## ⚠️ Important Reminders
384
+
385
+ - **No authentication required** - All endpoints shown are public
386
+ - **Rate limiting** - Add delays (0.5-1s) between requests in loops
387
+ - **Timeout handling** - Use appropriate timeouts (10-30s)
388
+ - **Error handling** - Check for HTTP errors and null responses
389
+ - **Respect the API** - Don't hammer the servers with concurrent requests
390
+
391
+ ## 📊 Summary
392
+
393
+ | Scenario | Items | Action |
394
+ |----------|-------|--------|
395
+ | Quick lookup | 1-5 | Use MCP tools |
396
+ | Comparison | 5-20 | Use MCP tools |
397
+ | Small screening | 20-50 | Ask user preference |
398
+ | Bulk screening | 50-200 | Provide script |
399
+ | Large analysis | 200+ | Provide script + pagination guide |
400
+
401
+ **Golden Rule**: If you're thinking about calling the same tool in a loop more than 20 times, provide a script instead.
402
+ """
403
+
404
+
405
+ @mcp.resource("avanza://docs/usage")
406
+ async def get_usage_guide() -> str:
407
+ """Get comprehensive usage guide for AI assistants.
408
+
409
+ URI: avanza://docs/usage
410
+
411
+ This resource provides critical guidance on:
412
+ - When to use MCP tools (small, interactive queries)
413
+ - When to provide scripts instead (bulk data fetching)
414
+ - How to write curl commands and Python scripts for users
415
+ - API endpoint reference for script writing
416
+
417
+ Returns:
418
+ Markdown-formatted usage guide
419
+ """
420
+ return USAGE_GUIDE
421
+
422
+
423
+ @mcp.resource("avanza://docs/quick-start")
424
+ async def get_quick_start() -> str:
425
+ """Get quick start guide focused on tool vs script decision.
426
+
427
+ URI: avanza://docs/quick-start
428
+
429
+ Returns:
430
+ Quick reference guide
431
+ """
432
+ return """# Avanza MCP - Quick Decision Guide
433
+
434
+ ## 🚦 When to Use What
435
+
436
+ ### Use MCP Tools (✅)
437
+ - Single lookups: "What's Tesla's price?"
438
+ - Small comparisons: "Compare these 5 funds"
439
+ - Quick exploration: "Find ETFs with USA exposure"
440
+ - Interactive analysis: Step-by-step investigation
441
+
442
+ ### Provide Script (📝)
443
+ - Bulk operations: "Analyze 100 stocks"
444
+ - Large screenings: "Get all certificates"
445
+ - Dataset building: "Fetch all Swedish funds"
446
+ - Repeated operations: "Daily price monitoring"
447
+
448
+ ## 📏 The Rule
449
+
450
+ ```
451
+ if items > 50:
452
+ provide_script()
453
+ elif items > 20:
454
+ ask_user_preference()
455
+ else:
456
+ use_mcp_tools()
457
+ ```
458
+
459
+ ## 🔧 Quick Script Templates
460
+
461
+ ### Fetch Multiple Stocks (Python)
462
+ ```python
463
+ import httpx
464
+ import asyncio
465
+
466
+ async def main():
467
+ stock_ids = ["5247", "5361", ...]
468
+ async with httpx.AsyncClient() as client:
469
+ for sid in stock_ids:
470
+ r = await client.get(f"https://www.avanza.se/_api/market-guide/stock/{sid}")
471
+ print(r.json()['name'], r.json()['quote']['last'])
472
+
473
+ asyncio.run(main())
474
+ ```
475
+
476
+ ### Bulk Filter (curl)
477
+ ```bash
478
+ curl 'https://www.avanza.se/_api/market-etf-filter/' \\
479
+ -H 'Content-Type: application/json' \\
480
+ --data-raw '{"filter":{},"offset":0,"limit":100,"sortBy":{"field":"name","order":"asc"}}'
481
+ ```
482
+
483
+ ## 💭 Example Responses
484
+
485
+ **User**: "Check Volvo stock"
486
+ **You**: ✅ Use `get_stock_quote("5247")`
487
+
488
+ **User**: "Compare 10 funds"
489
+ **You**: ✅ Use `get_fund_info()` for each
490
+
491
+ **User**: "Analyze 80 Swedish stocks"
492
+ **You**: 📝 "Here's a Python script to fetch all 80 stocks..."
493
+
494
+ **User**: "Screen all ETFs"
495
+ **You**: 📝 "Here's a curl command using filter_etfs endpoint..."
496
+
497
+ ## 🎯 Remember
498
+
499
+ MCP = Interactive exploration (1-20 items)
500
+ Script = Bulk processing (50+ items)
501
+
502
+ Read full guide: `avanza://docs/usage`
503
+ """