avanza-mcp 1.2.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.
- avanza_mcp/__init__.py +1 -1
- avanza_mcp/prompts/__init__.py +2 -1
- avanza_mcp/prompts/workflows.py +488 -0
- avanza_mcp/resources/__init__.py +2 -1
- avanza_mcp/resources/usage.py +503 -0
- {avanza_mcp-1.2.0.dist-info → avanza_mcp-1.3.0.dist-info}/METADATA +20 -1
- {avanza_mcp-1.2.0.dist-info → avanza_mcp-1.3.0.dist-info}/RECORD +10 -8
- {avanza_mcp-1.2.0.dist-info → avanza_mcp-1.3.0.dist-info}/WHEEL +0 -0
- {avanza_mcp-1.2.0.dist-info → avanza_mcp-1.3.0.dist-info}/entry_points.txt +0 -0
- {avanza_mcp-1.2.0.dist-info → avanza_mcp-1.3.0.dist-info}/licenses/LICENSE.md +0 -0
avanza_mcp/__init__.py
CHANGED
avanza_mcp/prompts/__init__.py
CHANGED
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
"""Workflow prompt templates emphasizing when to provide scripts vs using tools."""
|
|
2
|
+
|
|
3
|
+
from .. import mcp
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@mcp.prompt()
|
|
7
|
+
def bulk_data_script_guide(item_count: int, operation_type: str) -> str:
|
|
8
|
+
"""Guide for providing scripts for bulk data operations.
|
|
9
|
+
|
|
10
|
+
Use this when user requests data for 50+ items.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
item_count: Number of items to fetch
|
|
14
|
+
operation_type: Type of operation (e.g., "stock analysis", "ETF screening")
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
Prompt explaining why and how to provide a script
|
|
18
|
+
"""
|
|
19
|
+
return f"""For {operation_type} involving {item_count} items, I should provide a script instead of using MCP tools.
|
|
20
|
+
|
|
21
|
+
## Why Not Use MCP Tools?
|
|
22
|
+
|
|
23
|
+
Making {item_count} MCP tool calls would:
|
|
24
|
+
- Take 5-10 minutes to complete
|
|
25
|
+
- Overload the MCP connection
|
|
26
|
+
- Be inefficient and error-prone
|
|
27
|
+
|
|
28
|
+
## What to Provide Instead
|
|
29
|
+
|
|
30
|
+
I'll give you a ready-to-run script that:
|
|
31
|
+
- Fetches all {item_count} items in parallel (30-60 seconds)
|
|
32
|
+
- Saves data to a file for analysis
|
|
33
|
+
- Includes error handling and rate limiting
|
|
34
|
+
|
|
35
|
+
### Python Script Template (Recommended)
|
|
36
|
+
```python
|
|
37
|
+
import httpx
|
|
38
|
+
import asyncio
|
|
39
|
+
import json
|
|
40
|
+
from datetime import datetime
|
|
41
|
+
|
|
42
|
+
async def fetch_item(client, item_id):
|
|
43
|
+
\"\"\"Fetch single item with error handling.\"\"\"
|
|
44
|
+
try:
|
|
45
|
+
url = "https://www.avanza.se/_api/..." # Specific endpoint
|
|
46
|
+
response = await client.get(url, timeout=30.0)
|
|
47
|
+
response.raise_for_status()
|
|
48
|
+
return response.json()
|
|
49
|
+
except Exception as e:
|
|
50
|
+
print(f"Error fetching {{item_id}}: {{e}}")
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
async def main():
|
|
54
|
+
item_ids = [...] # Your {item_count} IDs
|
|
55
|
+
|
|
56
|
+
print(f"Fetching {{len(item_ids)}} items...")
|
|
57
|
+
|
|
58
|
+
async with httpx.AsyncClient() as client:
|
|
59
|
+
tasks = [fetch_item(client, iid) for iid in item_ids]
|
|
60
|
+
results = await asyncio.gather(*tasks)
|
|
61
|
+
|
|
62
|
+
# Filter out errors
|
|
63
|
+
valid_results = [r for r in results if r is not None]
|
|
64
|
+
|
|
65
|
+
# Save to file
|
|
66
|
+
output_file = f"data_{{datetime.now().strftime('%Y%m%d_%H%M%S')}}.json"
|
|
67
|
+
with open(output_file, 'w') as f:
|
|
68
|
+
json.dump(valid_results, f, indent=2)
|
|
69
|
+
|
|
70
|
+
print(f"✅ Fetched {{len(valid_results)}}/{{len(item_ids)}} items")
|
|
71
|
+
print(f"📁 Saved to {{output_file}}")
|
|
72
|
+
|
|
73
|
+
if __name__ == "__main__":
|
|
74
|
+
asyncio.run(main())
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### How to Run
|
|
78
|
+
```bash
|
|
79
|
+
# Install dependencies
|
|
80
|
+
pip install httpx
|
|
81
|
+
|
|
82
|
+
# Run the script
|
|
83
|
+
python fetch_data.py
|
|
84
|
+
|
|
85
|
+
# Analyze the results
|
|
86
|
+
# I can help you analyze the JSON file after you run this!
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Next Steps
|
|
90
|
+
|
|
91
|
+
After you run the script:
|
|
92
|
+
1. Share the output file with me (or key findings)
|
|
93
|
+
2. I can help analyze patterns, create visualizations, etc.
|
|
94
|
+
3. We can drill down into specific interesting items using MCP tools
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@mcp.prompt()
|
|
99
|
+
def decide_tool_or_script(user_request: str, estimated_items: int) -> str:
|
|
100
|
+
"""Help decide whether to use tools or provide a script.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
user_request: What the user wants to do
|
|
104
|
+
estimated_items: Estimated number of items to process
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Decision prompt
|
|
108
|
+
"""
|
|
109
|
+
if estimated_items <= 20:
|
|
110
|
+
approach = "use MCP tools"
|
|
111
|
+
reason = "small enough for interactive exploration"
|
|
112
|
+
elif estimated_items <= 50:
|
|
113
|
+
approach = "ask user preference"
|
|
114
|
+
reason = "medium size - tools work but script is faster"
|
|
115
|
+
else:
|
|
116
|
+
approach = "provide script"
|
|
117
|
+
reason = "too many for MCP tools - script is much more efficient"
|
|
118
|
+
|
|
119
|
+
return f"""Request: "{user_request}"
|
|
120
|
+
Estimated items: {estimated_items}
|
|
121
|
+
|
|
122
|
+
## Decision: {approach.upper()}
|
|
123
|
+
|
|
124
|
+
**Reason**: {reason}
|
|
125
|
+
|
|
126
|
+
**Approach**:
|
|
127
|
+
{"I'll use MCP tools to fetch data interactively." if estimated_items <= 20 else
|
|
128
|
+
"I'll ask if you prefer tools (interactive) or script (faster)." if estimated_items <= 50 else
|
|
129
|
+
"I'll provide a Python/bash script for efficient bulk fetching."}
|
|
130
|
+
|
|
131
|
+
{"" if estimated_items <= 20 else f'''
|
|
132
|
+
## Script Template
|
|
133
|
+
|
|
134
|
+
For {estimated_items} items, here's what I'll provide:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
# Efficient parallel fetching
|
|
138
|
+
import httpx
|
|
139
|
+
import asyncio
|
|
140
|
+
|
|
141
|
+
async def fetch_all(ids):
|
|
142
|
+
async with httpx.AsyncClient() as client:
|
|
143
|
+
tasks = [client.get(f"https://www.avanza.se/_api/.../{{id}}") for id in ids]
|
|
144
|
+
responses = await asyncio.gather(*tasks, return_exceptions=True)
|
|
145
|
+
return [r.json() for r in responses if hasattr(r, 'json')]
|
|
146
|
+
|
|
147
|
+
# Run and save
|
|
148
|
+
results = asyncio.run(fetch_all(your_{estimated_items}_ids))
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
This completes in ~30-60 seconds vs {estimated_items * 2}+ seconds with MCP tools.
|
|
152
|
+
'''}
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@mcp.prompt()
|
|
157
|
+
def filter_large_dataset(instrument_type: str, criteria: str) -> str:
|
|
158
|
+
"""Guide for filtering large datasets using API endpoints directly.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
instrument_type: Type (certificates, etfs, warrants, etc.)
|
|
162
|
+
criteria: Filtering criteria
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Prompt with curl/script for bulk filtering
|
|
166
|
+
"""
|
|
167
|
+
filter_endpoints = {
|
|
168
|
+
"certificates": "market-certificate-filter",
|
|
169
|
+
"etfs": "market-etf-filter",
|
|
170
|
+
"warrants": "market-warrant-filter",
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
endpoint = filter_endpoints.get(instrument_type.lower(), "market-etf-filter")
|
|
174
|
+
|
|
175
|
+
return f"""To screen {instrument_type} with criteria: {criteria}
|
|
176
|
+
|
|
177
|
+
## Use the Filter API Directly (Don't Loop Through MCP Tools)
|
|
178
|
+
|
|
179
|
+
Instead of calling MCP tools repeatedly, use the filter endpoint:
|
|
180
|
+
|
|
181
|
+
### Option 1: curl Command (Quick & Simple)
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
curl 'https://www.avanza.se/_api/{endpoint}/' \\
|
|
185
|
+
-H 'Content-Type: application/json' \\
|
|
186
|
+
--data-raw '{{
|
|
187
|
+
"filter": {{
|
|
188
|
+
{{"// Add your filters here based on criteria"}}
|
|
189
|
+
}},
|
|
190
|
+
"offset": 0,
|
|
191
|
+
"limit": 100,
|
|
192
|
+
"sortBy": {{"field": "name", "order": "asc"}}
|
|
193
|
+
}}' | jq '.' > results.json
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Option 2: Python Script (For Processing)
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
import httpx
|
|
200
|
+
import json
|
|
201
|
+
|
|
202
|
+
def filter_{instrument_type}(filters, limit=100):
|
|
203
|
+
\"\"\"Filter {instrument_type} directly via API.\"\"\"
|
|
204
|
+
url = "https://www.avanza.se/_api/{endpoint}/"
|
|
205
|
+
|
|
206
|
+
payload = {{
|
|
207
|
+
"filter": filters,
|
|
208
|
+
"offset": 0,
|
|
209
|
+
"limit": limit,
|
|
210
|
+
"sortBy": {{"field": "name", "order": "asc"}}
|
|
211
|
+
}}
|
|
212
|
+
|
|
213
|
+
response = httpx.post(url, json=payload, timeout=30.0)
|
|
214
|
+
return response.json()
|
|
215
|
+
|
|
216
|
+
# Example: Apply your criteria
|
|
217
|
+
filters = {{
|
|
218
|
+
# Map your criteria to filter parameters
|
|
219
|
+
}}
|
|
220
|
+
|
|
221
|
+
results = filter_{instrument_type}(filters, limit=100)
|
|
222
|
+
|
|
223
|
+
# Save
|
|
224
|
+
with open('{instrument_type}_filtered.json', 'w') as f:
|
|
225
|
+
json.dump(results, f, indent=2)
|
|
226
|
+
|
|
227
|
+
print(f"Found {{results.get('totalNumberOfOrderbooks', 0)}} matches")
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Available Filter Parameters
|
|
231
|
+
|
|
232
|
+
**For {instrument_type}**:
|
|
233
|
+
{_get_filter_params(instrument_type)}
|
|
234
|
+
|
|
235
|
+
## Pagination for Large Results
|
|
236
|
+
|
|
237
|
+
If results exceed 100:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
# Get next page
|
|
241
|
+
curl 'https://www.avanza.se/_api/{endpoint}/' \\
|
|
242
|
+
-H 'Content-Type: application/json' \\
|
|
243
|
+
--data-raw '{{
|
|
244
|
+
"filter": {{}},
|
|
245
|
+
"offset": 100, # Next page
|
|
246
|
+
"limit": 100,
|
|
247
|
+
"sortBy": {{"field": "name", "order": "asc"}}
|
|
248
|
+
}}'
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## After Fetching
|
|
252
|
+
|
|
253
|
+
Once you have the results:
|
|
254
|
+
1. I can help analyze patterns in the data
|
|
255
|
+
2. Pick interesting items for deep dives using MCP tools
|
|
256
|
+
3. Create comparisons or visualizations
|
|
257
|
+
"""
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _get_filter_params(instrument_type: str) -> str:
|
|
261
|
+
"""Get filter parameters for instrument type."""
|
|
262
|
+
params = {
|
|
263
|
+
"certificates": """
|
|
264
|
+
- `directions`: ["long", "short"]
|
|
265
|
+
- `leverages`: [1.0, 2.0, 3.0, ...]
|
|
266
|
+
- `issuers`: ["Valour", "WisdomTree", ...]
|
|
267
|
+
- `underlyingInstruments`: [orderbookIds]
|
|
268
|
+
""",
|
|
269
|
+
"etfs": """
|
|
270
|
+
- `exposures`: ["usa", "europe", "global", ...]
|
|
271
|
+
- `assetCategories`: ["stock", "bond", "commodity", ...]
|
|
272
|
+
- `riskScores`: ["risk_one", "risk_two", ...]
|
|
273
|
+
- `managementFee`: (use sortBy to filter)
|
|
274
|
+
""",
|
|
275
|
+
"warrants": """
|
|
276
|
+
- `directions`: ["long", "short"]
|
|
277
|
+
- `subTypes`: ["TURBO", "MINI", ...]
|
|
278
|
+
- `issuers`: ["Societe Generale", ...]
|
|
279
|
+
- `underlyingInstruments`: [orderbookIds]
|
|
280
|
+
""",
|
|
281
|
+
}
|
|
282
|
+
return params.get(instrument_type.lower(), "Check API documentation")
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
@mcp.prompt()
|
|
286
|
+
def analyze_vs_fetch(operation_description: str, requires_bulk_data: bool) -> str:
|
|
287
|
+
"""Distinguish between analysis (use tools) and fetching (use scripts).
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
operation_description: What the user wants to do
|
|
291
|
+
requires_bulk_data: Whether it needs 50+ items
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
Guidance prompt
|
|
295
|
+
"""
|
|
296
|
+
if requires_bulk_data:
|
|
297
|
+
return f"""Operation: "{operation_description}"
|
|
298
|
+
|
|
299
|
+
## This Requires Bulk Data Fetching
|
|
300
|
+
|
|
301
|
+
### Two-Step Approach:
|
|
302
|
+
|
|
303
|
+
**Step 1: Fetch Data (You Do This)**
|
|
304
|
+
I'll provide a script to fetch the data:
|
|
305
|
+
- Runs in 30-60 seconds
|
|
306
|
+
- Saves to JSON/CSV file
|
|
307
|
+
- Handles errors gracefully
|
|
308
|
+
|
|
309
|
+
**Step 2: Analyze Data (I Help With This)**
|
|
310
|
+
After you have the data:
|
|
311
|
+
- Share the file or key metrics
|
|
312
|
+
- I'll use MCP tools for specific deep dives
|
|
313
|
+
- We can explore patterns together
|
|
314
|
+
|
|
315
|
+
### Why This Approach?
|
|
316
|
+
|
|
317
|
+
- **Efficient**: Parallel fetching vs sequential MCP calls
|
|
318
|
+
- **Reliable**: Better error handling, can resume if failed
|
|
319
|
+
- **Flexible**: You own the data for other analyses
|
|
320
|
+
|
|
321
|
+
### Script I'll Provide
|
|
322
|
+
|
|
323
|
+
```python
|
|
324
|
+
# Fast parallel data fetcher
|
|
325
|
+
import httpx, asyncio, json
|
|
326
|
+
|
|
327
|
+
async def fetch_all():
|
|
328
|
+
ids = [...] # Your list
|
|
329
|
+
async with httpx.AsyncClient() as c:
|
|
330
|
+
tasks = [c.get(f"https://www.avanza.se/_api/.../{{id}}") for id in ids]
|
|
331
|
+
return await asyncio.gather(*tasks, return_exceptions=True)
|
|
332
|
+
|
|
333
|
+
results = asyncio.run(fetch_all())
|
|
334
|
+
with open('data.json', 'w') as f:
|
|
335
|
+
json.dump([r.json() for r in results if hasattr(r, 'json')], f)
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Next: Share Results & Analyze Together
|
|
339
|
+
"""
|
|
340
|
+
else:
|
|
341
|
+
return f"""Operation: "{operation_description}"
|
|
342
|
+
|
|
343
|
+
## This Can Use MCP Tools Directly
|
|
344
|
+
|
|
345
|
+
I'll use MCP tools interactively because:
|
|
346
|
+
- Small enough dataset (< 20 items)
|
|
347
|
+
- Interactive exploration is valuable
|
|
348
|
+
- Results available immediately
|
|
349
|
+
|
|
350
|
+
I'll proceed with using the appropriate MCP tools for this analysis.
|
|
351
|
+
"""
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
@mcp.prompt()
|
|
355
|
+
def script_template_selector(
|
|
356
|
+
task: str, data_source: str, output_format: str = "json"
|
|
357
|
+
) -> str:
|
|
358
|
+
"""Provide appropriate script template based on task.
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
task: What to accomplish
|
|
362
|
+
data_source: Where to get data (endpoint type)
|
|
363
|
+
output_format: Desired output format
|
|
364
|
+
|
|
365
|
+
Returns:
|
|
366
|
+
Complete script template
|
|
367
|
+
"""
|
|
368
|
+
return f"""Task: {task}
|
|
369
|
+
Data Source: {data_source}
|
|
370
|
+
Output: {output_format}
|
|
371
|
+
|
|
372
|
+
## Recommended Script
|
|
373
|
+
|
|
374
|
+
```python
|
|
375
|
+
#!/usr/bin/env python3
|
|
376
|
+
\"\"\"
|
|
377
|
+
{task}
|
|
378
|
+
|
|
379
|
+
Fetches data from Avanza's public API and saves to {output_format}.
|
|
380
|
+
No authentication required.
|
|
381
|
+
\"\"\"
|
|
382
|
+
|
|
383
|
+
import httpx
|
|
384
|
+
import asyncio
|
|
385
|
+
import json
|
|
386
|
+
from pathlib import Path
|
|
387
|
+
from datetime import datetime
|
|
388
|
+
|
|
389
|
+
# Configuration
|
|
390
|
+
BASE_URL = "https://www.avanza.se/_api"
|
|
391
|
+
OUTPUT_DIR = Path("avanza_data")
|
|
392
|
+
MAX_CONCURRENT = 10 # Limit concurrent requests
|
|
393
|
+
|
|
394
|
+
async def fetch_item(client, item_id, semaphore):
|
|
395
|
+
\"\"\"Fetch single item with rate limiting.\"\"\"
|
|
396
|
+
async with semaphore:
|
|
397
|
+
try:
|
|
398
|
+
url = f"{{BASE_URL}}/{data_source}/{{item_id}}"
|
|
399
|
+
response = await client.get(url, timeout=30.0)
|
|
400
|
+
response.raise_for_status()
|
|
401
|
+
return {{"id": item_id, "data": response.json(), "error": None}}
|
|
402
|
+
except Exception as e:
|
|
403
|
+
return {{"id": item_id, "data": None, "error": str(e)}}
|
|
404
|
+
|
|
405
|
+
async def main():
|
|
406
|
+
# TODO: Replace with your actual IDs
|
|
407
|
+
item_ids = [
|
|
408
|
+
# "5247", # Example: Volvo
|
|
409
|
+
# "5361", # Example: Ericsson
|
|
410
|
+
# Add your IDs here
|
|
411
|
+
]
|
|
412
|
+
|
|
413
|
+
if not item_ids:
|
|
414
|
+
print("⚠️ Please add item IDs to the script")
|
|
415
|
+
return
|
|
416
|
+
|
|
417
|
+
print(f"📊 Fetching {{len(item_ids)}} items...")
|
|
418
|
+
|
|
419
|
+
# Create output directory
|
|
420
|
+
OUTPUT_DIR.mkdir(exist_ok=True)
|
|
421
|
+
|
|
422
|
+
# Rate limiting semaphore
|
|
423
|
+
semaphore = asyncio.Semaphore(MAX_CONCURRENT)
|
|
424
|
+
|
|
425
|
+
# Fetch all items
|
|
426
|
+
async with httpx.AsyncClient() as client:
|
|
427
|
+
tasks = [fetch_item(client, iid, semaphore) for iid in item_ids]
|
|
428
|
+
results = await asyncio.gather(*tasks)
|
|
429
|
+
|
|
430
|
+
# Separate successful and failed
|
|
431
|
+
successful = [r for r in results if r["error"] is None]
|
|
432
|
+
failed = [r for r in results if r["error"] is not None]
|
|
433
|
+
|
|
434
|
+
# Save results
|
|
435
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
436
|
+
output_file = OUTPUT_DIR / f"results_{{timestamp}}.{output_format}"
|
|
437
|
+
|
|
438
|
+
with open(output_file, "w", encoding="utf-8") as f:
|
|
439
|
+
json.dump(
|
|
440
|
+
{{
|
|
441
|
+
"metadata": {{
|
|
442
|
+
"timestamp": timestamp,
|
|
443
|
+
"total": len(item_ids),
|
|
444
|
+
"successful": len(successful),
|
|
445
|
+
"failed": len(failed),
|
|
446
|
+
}},
|
|
447
|
+
"data": [r["data"] for r in successful],
|
|
448
|
+
"errors": [{{"id": r["id"], "error": r["error"]}} for r in failed],
|
|
449
|
+
}},
|
|
450
|
+
f,
|
|
451
|
+
indent=2,
|
|
452
|
+
ensure_ascii=False,
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
# Print summary
|
|
456
|
+
print(f"\\n✅ Successfully fetched: {{len(successful)}}/{{len(item_ids)}}")
|
|
457
|
+
if failed:
|
|
458
|
+
print(f"❌ Failed: {{len(failed)}}")
|
|
459
|
+
for fail in failed[:5]: # Show first 5 errors
|
|
460
|
+
print(f" - {{fail['id']}}: {{fail['error']}}")
|
|
461
|
+
print(f"\\n📁 Saved to: {{output_file}}")
|
|
462
|
+
|
|
463
|
+
if __name__ == "__main__":
|
|
464
|
+
asyncio.run(main())
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
## How to Use
|
|
468
|
+
|
|
469
|
+
1. **Save the script**: `save as fetch_data.py`
|
|
470
|
+
2. **Add your IDs**: Edit the `item_ids` list
|
|
471
|
+
3. **Install dependency**: `pip install httpx`
|
|
472
|
+
4. **Run**: `python fetch_data.py`
|
|
473
|
+
|
|
474
|
+
## What You Get
|
|
475
|
+
|
|
476
|
+
- JSON file with all results
|
|
477
|
+
- Error tracking for failed requests
|
|
478
|
+
- Timestamp for record keeping
|
|
479
|
+
- Summary of success/failure rates
|
|
480
|
+
|
|
481
|
+
## After Running
|
|
482
|
+
|
|
483
|
+
Share the results file and I can help you:
|
|
484
|
+
- Analyze patterns
|
|
485
|
+
- Create visualizations
|
|
486
|
+
- Filter and sort data
|
|
487
|
+
- Deep dive on specific items
|
|
488
|
+
"""
|
avanza_mcp/resources/__init__.py
CHANGED
|
@@ -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
|
+
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: avanza-mcp
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: MCP server for Avanza public market data API - Swedish stocks, funds, and more
|
|
5
5
|
License-File: LICENSE.md
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -95,10 +95,29 @@ The author of this software is not responsible for any indirect damages (foresee
|
|
|
95
95
|
|
|
96
96
|
## 💡 MCP Prompts
|
|
97
97
|
|
|
98
|
+
### Analysis Prompts
|
|
98
99
|
- `analyze_stock` - Comprehensive stock analysis workflow
|
|
99
100
|
- `compare_funds` - Multi-fund comparison template
|
|
100
101
|
- `screen_dividend_stocks` - Dividend stock screening
|
|
101
102
|
|
|
103
|
+
### Workflow Prompts (Teach AI Efficient Data Fetching)
|
|
104
|
+
- `search_and_analyze_instrument` - Guide for finding and analyzing instruments efficiently
|
|
105
|
+
- `filter_instruments_efficiently` - Guide for filtering large datasets with pagination
|
|
106
|
+
- `compare_multiple_instruments` - Guide for comparing instruments efficiently
|
|
107
|
+
- `explore_market_segment` - Guide for exploring market segments
|
|
108
|
+
- `get_historical_analysis` - Guide for analyzing historical data
|
|
109
|
+
- `screen_by_criteria` - Guide for screening by custom criteria
|
|
110
|
+
|
|
111
|
+
## 📚 MCP Resources
|
|
112
|
+
|
|
113
|
+
### Documentation Resources
|
|
114
|
+
- `avanza://docs/usage` - Comprehensive usage guide for AI assistants
|
|
115
|
+
- `avanza://docs/quick-start` - Quick reference for common tasks
|
|
116
|
+
|
|
117
|
+
### Instrument Resources
|
|
118
|
+
- `avanza://stock/{instrument_id}` - Get stock information as markdown
|
|
119
|
+
- `avanza://fund/{instrument_id}` - Get fund information as markdown
|
|
120
|
+
|
|
102
121
|
|
|
103
122
|
### Configuration for MCP Clients
|
|
104
123
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
avanza_mcp/__init__.py,sha256=
|
|
1
|
+
avanza_mcp/__init__.py,sha256=ccShZ9OeQ4V6UgFirUJZI9OV5cD8H93MgIIkfyO-bnw,1002
|
|
2
2
|
avanza_mcp/client/__init__.py,sha256=Yab-cUgQclyZgqdUkl7I15abQjEXVLoTvD3430oxpuY,515
|
|
3
3
|
avanza_mcp/client/base.py,sha256=92WrXDf0oTvE9gLJvEcbNkgbaT3rQkhoCAoGIpbaz0M,12853
|
|
4
4
|
avanza_mcp/client/endpoints.py,sha256=S7LK9Ab2ATHMEZ_680SbPbi5PogzbpGa-2USoMGthjo,2732
|
|
@@ -15,10 +15,12 @@ avanza_mcp/models/instrument_data.py,sha256=-5MJTCFy4W8zVU-oVjTBxWo55SzGG62577X3
|
|
|
15
15
|
avanza_mcp/models/search.py,sha256=HrP3V2T9Y1LBk_wwnNJdqg6XbHaz96AqvVUQ9iwuNyw,2816
|
|
16
16
|
avanza_mcp/models/stock.py,sha256=KR7SxeaiGe-7Me0-mXuqE37xwyhW94FPn8zjOtpyh8I,6420
|
|
17
17
|
avanza_mcp/models/warrant.py,sha256=k8uzeSOeFxPZuV9CE0vZwvOpmDZwlNto9PzPQKtzJ8o,2324
|
|
18
|
-
avanza_mcp/prompts/__init__.py,sha256=
|
|
18
|
+
avanza_mcp/prompts/__init__.py,sha256=6GOy34A-1ZIhMk7yFyIJ8pHnE8j42aaBRReiuAOVlBE,191
|
|
19
19
|
avanza_mcp/prompts/analysis.py,sha256=uW3P_jdupoNp7QsyjPsjXJjCNlR5646oYyVV9SMwXeM,3694
|
|
20
|
-
avanza_mcp/
|
|
20
|
+
avanza_mcp/prompts/workflows.py,sha256=x56DjSRQZvV-yNHGL5BXwgb8MCCyy-rYXrSsLp0KJzw,13414
|
|
21
|
+
avanza_mcp/resources/__init__.py,sha256=IgzNHfy_fXl-oUfzL10HctB8GC5jOUIY58z3uKeXUg8,193
|
|
21
22
|
avanza_mcp/resources/instruments.py,sha256=1pZbQ19NAWNMD_qmBOcniwf988jpFKUK4QiXrdOyYGw,3948
|
|
23
|
+
avanza_mcp/resources/usage.py,sha256=sGtlR_dF-nHvFYEQYYugmispCgeKnT7OiFd_YfDhvNw,13602
|
|
22
24
|
avanza_mcp/services/__init__.py,sha256=hCCYSVLvFglaGfNo4MXQP8mzsKHmV8yG_iNH0VsIsPs,190
|
|
23
25
|
avanza_mcp/services/market_data_service.py,sha256=EEjF1rwUAZ9ejLeLJJu49cntTaHuZ4opApJ8faEj6so,19212
|
|
24
26
|
avanza_mcp/services/search_service.py,sha256=op2BKokwraqoOSv0SdnLMFBJJODyqCAhOaFiCJ1hc30,1930
|
|
@@ -31,8 +33,8 @@ avanza_mcp/tools/instrument_data.py,sha256=EkE4dPSm1d4m1CeofCBdualwQqbPB8UtxCGon
|
|
|
31
33
|
avanza_mcp/tools/market_data.py,sha256=qqvnrnYpcvPucZlqvLHODh4DsRF5XPC7WmguDmaEzEk,16474
|
|
32
34
|
avanza_mcp/tools/search.py,sha256=srB7K-9VCJhB6Zvs4aLX_jfFekiMUIkYgOAp5IrCApg,3722
|
|
33
35
|
avanza_mcp/tools/warrants.py,sha256=jLaC3FK5X1DQcU_f5oXXP6asjTTTp2GfhDcE47uF94M,4515
|
|
34
|
-
avanza_mcp-1.
|
|
35
|
-
avanza_mcp-1.
|
|
36
|
-
avanza_mcp-1.
|
|
37
|
-
avanza_mcp-1.
|
|
38
|
-
avanza_mcp-1.
|
|
36
|
+
avanza_mcp-1.3.0.dist-info/METADATA,sha256=h_5cJ6f4AdJVNUXjYqDzObJQLSquipu5m-5BBSZ5viM,6058
|
|
37
|
+
avanza_mcp-1.3.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
38
|
+
avanza_mcp-1.3.0.dist-info/entry_points.txt,sha256=4MF6-imOIBHK-U5DLdRrufwrV3MXfqGG0OIA_e4Jl_M,47
|
|
39
|
+
avanza_mcp-1.3.0.dist-info/licenses/LICENSE.md,sha256=WVxy2qGCtophbFXxeAHHY0IksvRJLsUIuRgcGNtqH5Q,1065
|
|
40
|
+
avanza_mcp-1.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|