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.
@@ -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