avanza-mcp 1.0.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 +29 -0
- avanza_mcp/client/__init__.py +25 -0
- avanza_mcp/client/base.py +375 -0
- avanza_mcp/client/endpoints.py +41 -0
- avanza_mcp/client/exceptions.py +66 -0
- avanza_mcp/models/__init__.py +41 -0
- avanza_mcp/models/common.py +50 -0
- avanza_mcp/models/fund.py +246 -0
- avanza_mcp/models/search.py +123 -0
- avanza_mcp/models/stock.py +268 -0
- avanza_mcp/prompts/__init__.py +6 -0
- avanza_mcp/prompts/analysis.py +116 -0
- avanza_mcp/resources/__init__.py +6 -0
- avanza_mcp/resources/instruments.py +127 -0
- avanza_mcp/services/__init__.py +6 -0
- avanza_mcp/services/market_data_service.py +298 -0
- avanza_mcp/services/search_service.py +64 -0
- avanza_mcp/tools/__init__.py +8 -0
- avanza_mcp/tools/funds.py +267 -0
- avanza_mcp/tools/market_data.py +508 -0
- avanza_mcp/tools/search.py +119 -0
- avanza_mcp-1.0.0.dist-info/METADATA +99 -0
- avanza_mcp-1.0.0.dist-info/RECORD +26 -0
- avanza_mcp-1.0.0.dist-info/WHEEL +4 -0
- avanza_mcp-1.0.0.dist-info/entry_points.txt +2 -0
- avanza_mcp-1.0.0.dist-info/licenses/LICENSE.md +21 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"""Fund-specific MCP tools."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
from fastmcp import Context
|
|
6
|
+
|
|
7
|
+
from .. import mcp
|
|
8
|
+
from ..client import AvanzaClient
|
|
9
|
+
from ..services import MarketDataService
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@mcp.tool()
|
|
13
|
+
async def get_fund_sustainability(
|
|
14
|
+
ctx: Context,
|
|
15
|
+
instrument_id: str,
|
|
16
|
+
) -> dict:
|
|
17
|
+
"""Get fund sustainability and ESG (Environmental, Social, Governance) metrics.
|
|
18
|
+
|
|
19
|
+
Provides comprehensive ESG ratings, carbon metrics, product involvement data,
|
|
20
|
+
and sustainability development goals aligned with the fund.
|
|
21
|
+
|
|
22
|
+
Use search_instruments() with instrument_type="fund" to find fund IDs.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
ctx: MCP context for logging
|
|
26
|
+
instrument_id: Avanza fund ID from search results
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Sustainability metrics including:
|
|
30
|
+
- lowCarbon: Whether fund is low carbon
|
|
31
|
+
- esgScore: Overall ESG score
|
|
32
|
+
- environmentalScore: Environmental performance score
|
|
33
|
+
- socialScore: Social responsibility score
|
|
34
|
+
- governanceScore: Corporate governance score
|
|
35
|
+
- controversyScore: Controversy involvement score
|
|
36
|
+
- sustainabilityRating: Overall sustainability rating (1-5)
|
|
37
|
+
- productInvolvements: List of controversial product involvements
|
|
38
|
+
- sustainabilityDevelopmentGoals: UN SDG alignments
|
|
39
|
+
- Various fossil fuel and carbon involvement metrics
|
|
40
|
+
|
|
41
|
+
Examples:
|
|
42
|
+
Get ESG metrics for a fund:
|
|
43
|
+
>>> get_fund_sustainability(instrument_id="41567")
|
|
44
|
+
"""
|
|
45
|
+
ctx.info(f"Fetching fund sustainability for ID: {instrument_id}")
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
async with AvanzaClient() as client:
|
|
49
|
+
service = MarketDataService(client)
|
|
50
|
+
sustainability = await service.get_fund_sustainability(instrument_id)
|
|
51
|
+
|
|
52
|
+
ctx.info(
|
|
53
|
+
f"Retrieved sustainability data: ESG={sustainability.esgScore}, rating={sustainability.sustainabilityRating}"
|
|
54
|
+
)
|
|
55
|
+
return sustainability.model_dump(by_alias=True, exclude_none=True)
|
|
56
|
+
|
|
57
|
+
except Exception as e:
|
|
58
|
+
ctx.error(f"Failed to fetch fund sustainability: {str(e)}")
|
|
59
|
+
raise
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@mcp.tool()
|
|
63
|
+
async def get_fund_chart(
|
|
64
|
+
ctx: Context,
|
|
65
|
+
instrument_id: str,
|
|
66
|
+
time_period: Literal[
|
|
67
|
+
"one_week",
|
|
68
|
+
"one_month",
|
|
69
|
+
"three_months",
|
|
70
|
+
"one_year",
|
|
71
|
+
"three_years",
|
|
72
|
+
"five_years",
|
|
73
|
+
"this_year",
|
|
74
|
+
] = "three_years",
|
|
75
|
+
) -> dict:
|
|
76
|
+
"""Get fund chart data with historical performance.
|
|
77
|
+
|
|
78
|
+
Returns time series data showing fund performance over the selected period.
|
|
79
|
+
Perfect for visualizing fund NAV history and performance trends.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
ctx: MCP context for logging
|
|
83
|
+
instrument_id: Avanza fund ID from search results
|
|
84
|
+
time_period: Time period for chart data. Options:
|
|
85
|
+
- "one_week": Past week
|
|
86
|
+
- "one_month": Past month
|
|
87
|
+
- "three_months": Past 3 months
|
|
88
|
+
- "one_year": Past year
|
|
89
|
+
- "three_years": Past 3 years (default)
|
|
90
|
+
- "five_years": Past 5 years
|
|
91
|
+
- "this_year": Year to date
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Chart data with:
|
|
95
|
+
- id: Fund identifier
|
|
96
|
+
- name: Fund name
|
|
97
|
+
- dataSerie: Array of data points with timestamp (x) and value (y)
|
|
98
|
+
- fromDate: Start date of chart data
|
|
99
|
+
- toDate: End date of chart data
|
|
100
|
+
|
|
101
|
+
Examples:
|
|
102
|
+
Get 3-year performance chart:
|
|
103
|
+
>>> get_fund_chart(instrument_id="41567", time_period="three_years")
|
|
104
|
+
|
|
105
|
+
Get year-to-date performance:
|
|
106
|
+
>>> get_fund_chart(instrument_id="41567", time_period="this_year")
|
|
107
|
+
"""
|
|
108
|
+
ctx.info(f"Fetching fund chart for ID: {instrument_id} (time_period={time_period})")
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
async with AvanzaClient() as client:
|
|
112
|
+
service = MarketDataService(client)
|
|
113
|
+
chart = await service.get_fund_chart(instrument_id, time_period)
|
|
114
|
+
|
|
115
|
+
data_points = len(chart.dataSerie)
|
|
116
|
+
ctx.info(f"Retrieved chart with {data_points} data points")
|
|
117
|
+
return chart.model_dump(by_alias=True, exclude_none=True)
|
|
118
|
+
|
|
119
|
+
except Exception as e:
|
|
120
|
+
ctx.error(f"Failed to fetch fund chart: {str(e)}")
|
|
121
|
+
raise
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@mcp.tool()
|
|
125
|
+
async def get_fund_chart_periods(
|
|
126
|
+
ctx: Context,
|
|
127
|
+
instrument_id: str,
|
|
128
|
+
) -> dict:
|
|
129
|
+
"""Get available fund performance periods with returns.
|
|
130
|
+
|
|
131
|
+
Returns a list of all available time periods with the fund's performance
|
|
132
|
+
(percentage change) for each period. Useful for quick performance overview.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
ctx: MCP context for logging
|
|
136
|
+
instrument_id: Avanza fund ID from search results
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
List of performance periods, each containing:
|
|
140
|
+
- timePeriod: Period identifier (e.g., "one_year", "three_years")
|
|
141
|
+
- change: Performance change as percentage
|
|
142
|
+
- startDate: Start date for the period
|
|
143
|
+
|
|
144
|
+
Examples:
|
|
145
|
+
Get all available performance periods:
|
|
146
|
+
>>> get_fund_chart_periods(instrument_id="41567")
|
|
147
|
+
"""
|
|
148
|
+
ctx.info(f"Fetching fund chart periods for ID: {instrument_id}")
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
async with AvanzaClient() as client:
|
|
152
|
+
service = MarketDataService(client)
|
|
153
|
+
periods = await service.get_fund_chart_periods(instrument_id)
|
|
154
|
+
|
|
155
|
+
ctx.info(f"Retrieved {len(periods)} time periods")
|
|
156
|
+
return {
|
|
157
|
+
"periods": [period.model_dump(by_alias=True, exclude_none=True) for period in periods]
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
except Exception as e:
|
|
161
|
+
ctx.error(f"Failed to fetch fund chart periods: {str(e)}")
|
|
162
|
+
raise
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@mcp.tool()
|
|
166
|
+
async def get_fund_description(
|
|
167
|
+
ctx: Context,
|
|
168
|
+
instrument_id: str,
|
|
169
|
+
) -> dict:
|
|
170
|
+
"""Get detailed fund description and category information.
|
|
171
|
+
|
|
172
|
+
Provides comprehensive textual description of the fund, its investment
|
|
173
|
+
strategy, and detailed category classification.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
ctx: MCP context for logging
|
|
177
|
+
instrument_id: Avanza fund ID from search results
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Fund description data with:
|
|
181
|
+
- response: Main fund description text
|
|
182
|
+
- heading: Description heading/title
|
|
183
|
+
- detailedCategoryDescription: Detailed category explanation
|
|
184
|
+
|
|
185
|
+
Examples:
|
|
186
|
+
Get fund description:
|
|
187
|
+
>>> get_fund_description(instrument_id="41567")
|
|
188
|
+
"""
|
|
189
|
+
ctx.info(f"Fetching fund description for ID: {instrument_id}")
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
async with AvanzaClient() as client:
|
|
193
|
+
service = MarketDataService(client)
|
|
194
|
+
description = await service.get_fund_description(instrument_id)
|
|
195
|
+
|
|
196
|
+
ctx.info("Retrieved fund description")
|
|
197
|
+
return description.model_dump(by_alias=True, exclude_none=True)
|
|
198
|
+
|
|
199
|
+
except Exception as e:
|
|
200
|
+
ctx.error(f"Failed to fetch fund description: {str(e)}")
|
|
201
|
+
raise
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@mcp.tool()
|
|
205
|
+
async def get_fund_holdings(
|
|
206
|
+
ctx: Context,
|
|
207
|
+
instrument_id: str,
|
|
208
|
+
) -> dict:
|
|
209
|
+
"""Get fund portfolio holdings and allocation breakdown.
|
|
210
|
+
|
|
211
|
+
Returns the fund's portfolio composition including geographic allocation,
|
|
212
|
+
sector allocation, and top holdings. Useful for understanding what the
|
|
213
|
+
fund invests in.
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
ctx: MCP context for logging
|
|
217
|
+
instrument_id: Avanza fund ID from search results
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
Portfolio allocation data with:
|
|
221
|
+
- countryChartData: Geographic allocation by country (name, y=percentage)
|
|
222
|
+
- sectorChartData: Sector allocation (name, y=percentage)
|
|
223
|
+
- holdingChartData: Top holdings (name, y=percentage)
|
|
224
|
+
- portfolioDate: Date of portfolio data
|
|
225
|
+
|
|
226
|
+
Examples:
|
|
227
|
+
Get holdings for a fund:
|
|
228
|
+
>>> get_fund_holdings(instrument_id="878733")
|
|
229
|
+
"""
|
|
230
|
+
ctx.info(f"Fetching fund holdings for ID: {instrument_id}")
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
async with AvanzaClient() as client:
|
|
234
|
+
service = MarketDataService(client)
|
|
235
|
+
fund_info = await service.get_fund_info(instrument_id)
|
|
236
|
+
|
|
237
|
+
holdings = {
|
|
238
|
+
"countryChartData": [
|
|
239
|
+
c.model_dump(by_alias=True, exclude_none=True)
|
|
240
|
+
for c in fund_info.country_chart_data
|
|
241
|
+
],
|
|
242
|
+
"sectorChartData": [
|
|
243
|
+
s.model_dump(by_alias=True, exclude_none=True)
|
|
244
|
+
for s in fund_info.sector_chart_data
|
|
245
|
+
],
|
|
246
|
+
"holdingChartData": [
|
|
247
|
+
h.model_dump(by_alias=True, exclude_none=True)
|
|
248
|
+
for h in fund_info.holding_chart_data
|
|
249
|
+
],
|
|
250
|
+
"portfolioDate": (
|
|
251
|
+
fund_info.portfolio_date.isoformat()
|
|
252
|
+
if fund_info.portfolio_date
|
|
253
|
+
else None
|
|
254
|
+
),
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
countries = len(holdings["countryChartData"])
|
|
258
|
+
sectors = len(holdings["sectorChartData"])
|
|
259
|
+
top_holdings = len(holdings["holdingChartData"])
|
|
260
|
+
ctx.info(
|
|
261
|
+
f"Retrieved holdings: {countries} countries, {sectors} sectors, {top_holdings} top holdings"
|
|
262
|
+
)
|
|
263
|
+
return holdings
|
|
264
|
+
|
|
265
|
+
except Exception as e:
|
|
266
|
+
ctx.error(f"Failed to fetch fund holdings: {str(e)}")
|
|
267
|
+
raise
|