mseep-a-share-mcp 0.1.1__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.
- __init__.py +1 -0
- baostock_data_source.py +689 -0
- data_source_interface.py +126 -0
- formatting/__init__.py +1 -0
- formatting/markdown_formatter.py +65 -0
- mseep_a_share_mcp-0.1.1.dist-info/METADATA +46 -0
- mseep_a_share_mcp-0.1.1.dist-info/RECORD +20 -0
- mseep_a_share_mcp-0.1.1.dist-info/WHEEL +5 -0
- mseep_a_share_mcp-0.1.1.dist-info/licenses/LICENSE +21 -0
- mseep_a_share_mcp-0.1.1.dist-info/top_level.txt +6 -0
- tools/__init__.py +1 -0
- tools/analysis.py +169 -0
- tools/base.py +161 -0
- tools/date_utils.py +180 -0
- tools/financial_reports.py +201 -0
- tools/indices.py +103 -0
- tools/macroeconomic.py +146 -0
- tools/market_overview.py +99 -0
- tools/stock_market.py +242 -0
- utils.py +69 -0
tools/date_utils.py
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
"""
|
2
|
+
Date utility tools for MCP server.
|
3
|
+
Contains tools for retrieving current date and latest trading date.
|
4
|
+
"""
|
5
|
+
import logging
|
6
|
+
from datetime import datetime, timedelta
|
7
|
+
import calendar
|
8
|
+
|
9
|
+
from mcp.server.fastmcp import FastMCP
|
10
|
+
from src.data_source_interface import FinancialDataSource
|
11
|
+
|
12
|
+
logger = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
def register_date_utils_tools(app: FastMCP, active_data_source: FinancialDataSource):
|
16
|
+
"""
|
17
|
+
Register date utility tools with the MCP app.
|
18
|
+
|
19
|
+
Args:
|
20
|
+
app: The FastMCP app instance
|
21
|
+
active_data_source: The active financial data source
|
22
|
+
"""
|
23
|
+
|
24
|
+
# @app.tool()
|
25
|
+
# def get_current_date() -> str:
|
26
|
+
# """
|
27
|
+
# 获取当前日期,可用于查询最新数据。
|
28
|
+
|
29
|
+
# Returns:
|
30
|
+
# 当前日期,格式为'YYYY-MM-DD'。
|
31
|
+
# """
|
32
|
+
# logger.info("Tool 'get_current_date' called")
|
33
|
+
# current_date = datetime.now().strftime("%Y-%m-%d")
|
34
|
+
# logger.info(f"Returning current date: {current_date}")
|
35
|
+
# return current_date
|
36
|
+
|
37
|
+
@app.tool()
|
38
|
+
def get_latest_trading_date() -> str:
|
39
|
+
"""
|
40
|
+
获取最近的交易日期。如果当天是交易日,则返回当天日期;否则返回最近的交易日。
|
41
|
+
|
42
|
+
Returns:
|
43
|
+
最近的交易日期,格式为'YYYY-MM-DD'。
|
44
|
+
"""
|
45
|
+
logger.info("Tool 'get_latest_trading_date' called")
|
46
|
+
try:
|
47
|
+
today = datetime.now().strftime("%Y-%m-%d")
|
48
|
+
# 获取当前日期前后一周的交易日历
|
49
|
+
start_date = (datetime.now().replace(day=1)).strftime("%Y-%m-%d")
|
50
|
+
end_date = (datetime.now().replace(day=28)).strftime("%Y-%m-%d")
|
51
|
+
|
52
|
+
df = active_data_source.get_trade_dates(
|
53
|
+
start_date=start_date, end_date=end_date)
|
54
|
+
|
55
|
+
# 筛选出最近的交易日
|
56
|
+
valid_trading_days = df[df['is_trading_day']
|
57
|
+
== '1']['calendar_date'].tolist()
|
58
|
+
|
59
|
+
# 找出小于等于今天的最大日期
|
60
|
+
latest_trading_date = None
|
61
|
+
for date in valid_trading_days:
|
62
|
+
if date <= today and (latest_trading_date is None or date > latest_trading_date):
|
63
|
+
latest_trading_date = date
|
64
|
+
|
65
|
+
if latest_trading_date:
|
66
|
+
logger.info(
|
67
|
+
f"Latest trading date found: {latest_trading_date}")
|
68
|
+
return latest_trading_date
|
69
|
+
else:
|
70
|
+
logger.warning(
|
71
|
+
"No trading dates found before today, returning today's date")
|
72
|
+
return today
|
73
|
+
|
74
|
+
except Exception as e:
|
75
|
+
logger.exception(f"Error determining latest trading date: {e}")
|
76
|
+
return datetime.now().strftime("%Y-%m-%d")
|
77
|
+
|
78
|
+
@app.tool()
|
79
|
+
def get_market_analysis_timeframe(period: str = "recent") -> str:
|
80
|
+
"""
|
81
|
+
获取适合市场分析的时间范围,基于当前真实日期而不是训练数据。
|
82
|
+
这个工具应该在进行市场分析或大盘分析时首先调用,以确保使用最新的实际数据。
|
83
|
+
|
84
|
+
Args:
|
85
|
+
period: 时间范围类型,可选值:
|
86
|
+
"recent": 最近1-2个月(默认)
|
87
|
+
"quarter": 最近一个季度
|
88
|
+
"half_year": 最近半年
|
89
|
+
"year": 最近一年
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
包含分析时间范围的详细描述字符串,格式为"YYYY年M月-YYYY年M月"。
|
93
|
+
"""
|
94
|
+
logger.info(
|
95
|
+
f"Tool 'get_market_analysis_timeframe' called with period={period}")
|
96
|
+
|
97
|
+
now = datetime.now()
|
98
|
+
end_date = now
|
99
|
+
|
100
|
+
# 根据请求的时间段确定开始日期
|
101
|
+
if period == "recent":
|
102
|
+
# 最近1-2个月
|
103
|
+
if now.day < 15:
|
104
|
+
# 如果当前是月初,看前两个月
|
105
|
+
if now.month == 1:
|
106
|
+
start_date = datetime(now.year - 1, 11, 1) # 前年11月
|
107
|
+
middle_date = datetime(now.year - 1, 12, 1) # 前年12月
|
108
|
+
elif now.month == 2:
|
109
|
+
start_date = datetime(now.year, 1, 1) # 今年1月
|
110
|
+
middle_date = start_date
|
111
|
+
else:
|
112
|
+
start_date = datetime(now.year, now.month - 2, 1) # 两个月前
|
113
|
+
middle_date = datetime(now.year, now.month - 1, 1) # 上个月
|
114
|
+
else:
|
115
|
+
# 如果当前是月中或月末,看前一个月到现在
|
116
|
+
if now.month == 1:
|
117
|
+
start_date = datetime(now.year - 1, 12, 1) # 前年12月
|
118
|
+
middle_date = start_date
|
119
|
+
else:
|
120
|
+
start_date = datetime(now.year, now.month - 1, 1) # 上个月
|
121
|
+
middle_date = start_date
|
122
|
+
|
123
|
+
elif period == "quarter":
|
124
|
+
# 最近一个季度 (约3个月)
|
125
|
+
if now.month <= 3:
|
126
|
+
start_date = datetime(now.year - 1, now.month + 9, 1)
|
127
|
+
else:
|
128
|
+
start_date = datetime(now.year, now.month - 3, 1)
|
129
|
+
middle_date = start_date
|
130
|
+
|
131
|
+
elif period == "half_year":
|
132
|
+
# 最近半年
|
133
|
+
if now.month <= 6:
|
134
|
+
start_date = datetime(now.year - 1, now.month + 6, 1)
|
135
|
+
else:
|
136
|
+
start_date = datetime(now.year, now.month - 6, 1)
|
137
|
+
middle_date = datetime(start_date.year, start_date.month + 3, 1) if start_date.month <= 9 else \
|
138
|
+
datetime(start_date.year + 1, start_date.month - 9, 1)
|
139
|
+
|
140
|
+
elif period == "year":
|
141
|
+
# 最近一年
|
142
|
+
start_date = datetime(now.year - 1, now.month, 1)
|
143
|
+
middle_date = datetime(start_date.year, start_date.month + 6, 1) if start_date.month <= 6 else \
|
144
|
+
datetime(start_date.year + 1, start_date.month - 6, 1)
|
145
|
+
else:
|
146
|
+
# 默认为最近1个月
|
147
|
+
if now.month == 1:
|
148
|
+
start_date = datetime(now.year - 1, 12, 1)
|
149
|
+
else:
|
150
|
+
start_date = datetime(now.year, now.month - 1, 1)
|
151
|
+
middle_date = start_date
|
152
|
+
|
153
|
+
# 格式化为用户友好的显示
|
154
|
+
def get_month_end_day(year, month):
|
155
|
+
return calendar.monthrange(year, month)[1]
|
156
|
+
|
157
|
+
# 确保结束日期不超过当前日期
|
158
|
+
end_day = min(get_month_end_day(
|
159
|
+
end_date.year, end_date.month), end_date.day)
|
160
|
+
end_display_date = f"{end_date.year}年{end_date.month}月"
|
161
|
+
end_iso_date = f"{end_date.year}-{end_date.month:02d}-{end_day:02d}"
|
162
|
+
|
163
|
+
# 开始日期显示
|
164
|
+
start_display_date = f"{start_date.year}年{start_date.month}月"
|
165
|
+
start_iso_date = f"{start_date.year}-{start_date.month:02d}-01"
|
166
|
+
|
167
|
+
# 如果跨年或时间段较长,添加年份显示
|
168
|
+
if start_date.year != end_date.year:
|
169
|
+
date_range = f"{start_date.year}年{start_date.month}月-{end_date.year}年{end_date.month}月"
|
170
|
+
elif middle_date.month != start_date.month and middle_date.month != end_date.month:
|
171
|
+
# 如果是季度或半年,显示中间月份
|
172
|
+
date_range = f"{start_date.year}年{start_date.month}月-{middle_date.month}月-{end_date.month}月"
|
173
|
+
elif start_date.month != end_date.month:
|
174
|
+
date_range = f"{start_date.year}年{start_date.month}月-{end_date.month}月"
|
175
|
+
else:
|
176
|
+
date_range = f"{start_date.year}年{start_date.month}月"
|
177
|
+
|
178
|
+
result = f"{date_range} (ISO日期范围: {start_iso_date} 至 {end_iso_date})"
|
179
|
+
logger.info(f"Generated market analysis timeframe: {result}")
|
180
|
+
return result
|
@@ -0,0 +1,201 @@
|
|
1
|
+
"""
|
2
|
+
Financial report related tools for MCP server.
|
3
|
+
"""
|
4
|
+
import logging
|
5
|
+
from typing import List, Optional
|
6
|
+
|
7
|
+
from mcp.server.fastmcp import FastMCP
|
8
|
+
from src.data_source_interface import FinancialDataSource
|
9
|
+
from src.tools.base import call_financial_data_tool
|
10
|
+
|
11
|
+
logger = logging.getLogger(__name__)
|
12
|
+
|
13
|
+
|
14
|
+
def register_financial_report_tools(app: FastMCP, active_data_source: FinancialDataSource):
|
15
|
+
"""
|
16
|
+
Register financial report related tools with the MCP app.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
app: The FastMCP app instance
|
20
|
+
active_data_source: The active financial data source
|
21
|
+
"""
|
22
|
+
|
23
|
+
@app.tool()
|
24
|
+
def get_profit_data(code: str, year: str, quarter: int) -> str:
|
25
|
+
"""
|
26
|
+
Fetches quarterly profitability data (e.g., ROE, net profit margin) for a stock.
|
27
|
+
|
28
|
+
Args:
|
29
|
+
code: The stock code (e.g., 'sh.600000').
|
30
|
+
year: The 4-digit year (e.g., '2023').
|
31
|
+
quarter: The quarter (1, 2, 3, or 4).
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
Markdown table with profitability data or an error message.
|
35
|
+
"""
|
36
|
+
return call_financial_data_tool(
|
37
|
+
"get_profit_data",
|
38
|
+
active_data_source.get_profit_data,
|
39
|
+
"Profitability",
|
40
|
+
code, year, quarter
|
41
|
+
)
|
42
|
+
|
43
|
+
@app.tool()
|
44
|
+
def get_operation_data(code: str, year: str, quarter: int) -> str:
|
45
|
+
"""
|
46
|
+
Fetches quarterly operation capability data (e.g., turnover ratios) for a stock.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
code: The stock code (e.g., 'sh.600000').
|
50
|
+
year: The 4-digit year (e.g., '2023').
|
51
|
+
quarter: The quarter (1, 2, 3, or 4).
|
52
|
+
|
53
|
+
Returns:
|
54
|
+
Markdown table with operation capability data or an error message.
|
55
|
+
"""
|
56
|
+
return call_financial_data_tool(
|
57
|
+
"get_operation_data",
|
58
|
+
active_data_source.get_operation_data,
|
59
|
+
"Operation Capability",
|
60
|
+
code, year, quarter
|
61
|
+
)
|
62
|
+
|
63
|
+
@app.tool()
|
64
|
+
def get_growth_data(code: str, year: str, quarter: int) -> str:
|
65
|
+
"""
|
66
|
+
Fetches quarterly growth capability data (e.g., YOY growth rates) for a stock.
|
67
|
+
|
68
|
+
Args:
|
69
|
+
code: The stock code (e.g., 'sh.600000').
|
70
|
+
year: The 4-digit year (e.g., '2023').
|
71
|
+
quarter: The quarter (1, 2, 3, or 4).
|
72
|
+
|
73
|
+
Returns:
|
74
|
+
Markdown table with growth capability data or an error message.
|
75
|
+
"""
|
76
|
+
return call_financial_data_tool(
|
77
|
+
"get_growth_data",
|
78
|
+
active_data_source.get_growth_data,
|
79
|
+
"Growth Capability",
|
80
|
+
code, year, quarter
|
81
|
+
)
|
82
|
+
|
83
|
+
@app.tool()
|
84
|
+
def get_balance_data(code: str, year: str, quarter: int) -> str:
|
85
|
+
"""
|
86
|
+
Fetches quarterly balance sheet / solvency data (e.g., current ratio, debt ratio) for a stock.
|
87
|
+
|
88
|
+
Args:
|
89
|
+
code: The stock code (e.g., 'sh.600000').
|
90
|
+
year: The 4-digit year (e.g., '2023').
|
91
|
+
quarter: The quarter (1, 2, 3, or 4).
|
92
|
+
|
93
|
+
Returns:
|
94
|
+
Markdown table with balance sheet data or an error message.
|
95
|
+
"""
|
96
|
+
return call_financial_data_tool(
|
97
|
+
"get_balance_data",
|
98
|
+
active_data_source.get_balance_data,
|
99
|
+
"Balance Sheet",
|
100
|
+
code, year, quarter
|
101
|
+
)
|
102
|
+
|
103
|
+
@app.tool()
|
104
|
+
def get_cash_flow_data(code: str, year: str, quarter: int) -> str:
|
105
|
+
"""
|
106
|
+
Fetches quarterly cash flow data (e.g., CFO/Operating Revenue ratio) for a stock.
|
107
|
+
|
108
|
+
Args:
|
109
|
+
code: The stock code (e.g., 'sh.600000').
|
110
|
+
year: The 4-digit year (e.g., '2023').
|
111
|
+
quarter: The quarter (1, 2, 3, or 4).
|
112
|
+
|
113
|
+
Returns:
|
114
|
+
Markdown table with cash flow data or an error message.
|
115
|
+
"""
|
116
|
+
return call_financial_data_tool(
|
117
|
+
"get_cash_flow_data",
|
118
|
+
active_data_source.get_cash_flow_data,
|
119
|
+
"Cash Flow",
|
120
|
+
code, year, quarter
|
121
|
+
)
|
122
|
+
|
123
|
+
@app.tool()
|
124
|
+
def get_dupont_data(code: str, year: str, quarter: int) -> str:
|
125
|
+
"""
|
126
|
+
Fetches quarterly DuPont analysis data (ROE decomposition) for a stock.
|
127
|
+
|
128
|
+
Args:
|
129
|
+
code: The stock code (e.g., 'sh.600000').
|
130
|
+
year: The 4-digit year (e.g., '2023').
|
131
|
+
quarter: The quarter (1, 2, 3, or 4).
|
132
|
+
|
133
|
+
Returns:
|
134
|
+
Markdown table with DuPont analysis data or an error message.
|
135
|
+
"""
|
136
|
+
return call_financial_data_tool(
|
137
|
+
"get_dupont_data",
|
138
|
+
active_data_source.get_dupont_data,
|
139
|
+
"DuPont Analysis",
|
140
|
+
code, year, quarter
|
141
|
+
)
|
142
|
+
|
143
|
+
@app.tool()
|
144
|
+
def get_performance_express_report(code: str, start_date: str, end_date: str) -> str:
|
145
|
+
"""
|
146
|
+
Fetches performance express reports (业绩快报) for a stock within a date range.
|
147
|
+
Note: Companies are not required to publish these except in specific cases.
|
148
|
+
|
149
|
+
Args:
|
150
|
+
code: The stock code (e.g., 'sh.600000').
|
151
|
+
start_date: Start date (for report publication/update) in 'YYYY-MM-DD' format.
|
152
|
+
end_date: End date (for report publication/update) in 'YYYY-MM-DD' format.
|
153
|
+
|
154
|
+
Returns:
|
155
|
+
Markdown table with performance express report data or an error message.
|
156
|
+
"""
|
157
|
+
logger.info(
|
158
|
+
f"Tool 'get_performance_express_report' called for {code} ({start_date} to {end_date})")
|
159
|
+
try:
|
160
|
+
# Add date validation if desired
|
161
|
+
df = active_data_source.get_performance_express_report(
|
162
|
+
code=code, start_date=start_date, end_date=end_date)
|
163
|
+
logger.info(
|
164
|
+
f"Successfully retrieved performance express reports for {code}.")
|
165
|
+
from src.formatting.markdown_formatter import format_df_to_markdown
|
166
|
+
return format_df_to_markdown(df)
|
167
|
+
|
168
|
+
except Exception as e:
|
169
|
+
logger.exception(
|
170
|
+
f"Exception processing get_performance_express_report for {code}: {e}")
|
171
|
+
return f"Error: An unexpected error occurred: {e}"
|
172
|
+
|
173
|
+
@app.tool()
|
174
|
+
def get_forecast_report(code: str, start_date: str, end_date: str) -> str:
|
175
|
+
"""
|
176
|
+
Fetches performance forecast reports (业绩预告) for a stock within a date range.
|
177
|
+
Note: Companies are not required to publish these except in specific cases.
|
178
|
+
|
179
|
+
Args:
|
180
|
+
code: The stock code (e.g., 'sh.600000').
|
181
|
+
start_date: Start date (for report publication/update) in 'YYYY-MM-DD' format.
|
182
|
+
end_date: End date (for report publication/update) in 'YYYY-MM-DD' format.
|
183
|
+
|
184
|
+
Returns:
|
185
|
+
Markdown table with performance forecast report data or an error message.
|
186
|
+
"""
|
187
|
+
logger.info(
|
188
|
+
f"Tool 'get_forecast_report' called for {code} ({start_date} to {end_date})")
|
189
|
+
try:
|
190
|
+
# Add date validation if desired
|
191
|
+
df = active_data_source.get_forecast_report(
|
192
|
+
code=code, start_date=start_date, end_date=end_date)
|
193
|
+
logger.info(
|
194
|
+
f"Successfully retrieved performance forecast reports for {code}.")
|
195
|
+
from src.formatting.markdown_formatter import format_df_to_markdown
|
196
|
+
return format_df_to_markdown(df)
|
197
|
+
|
198
|
+
except Exception as e:
|
199
|
+
logger.exception(
|
200
|
+
f"Exception processing get_forecast_report for {code}: {e}")
|
201
|
+
return f"Error: An unexpected error occurred: {e}"
|
tools/indices.py
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
"""
|
2
|
+
Index related tools for MCP server.
|
3
|
+
Contains tools for fetching index constituent stocks.
|
4
|
+
"""
|
5
|
+
import logging
|
6
|
+
from typing import Optional
|
7
|
+
|
8
|
+
from mcp.server.fastmcp import FastMCP
|
9
|
+
from src.data_source_interface import FinancialDataSource
|
10
|
+
from src.tools.base import call_index_constituent_tool
|
11
|
+
|
12
|
+
logger = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
def register_index_tools(app: FastMCP, active_data_source: FinancialDataSource):
|
16
|
+
"""
|
17
|
+
Register index related tools with the MCP app.
|
18
|
+
|
19
|
+
Args:
|
20
|
+
app: The FastMCP app instance
|
21
|
+
active_data_source: The active financial data source
|
22
|
+
"""
|
23
|
+
|
24
|
+
@app.tool()
|
25
|
+
def get_stock_industry(code: Optional[str] = None, date: Optional[str] = None) -> str:
|
26
|
+
"""
|
27
|
+
Fetches industry classification for a specific stock or all stocks on a given date.
|
28
|
+
|
29
|
+
Args:
|
30
|
+
code: Optional. The stock code (e.g., 'sh.600000'). If None, fetches for all stocks.
|
31
|
+
date: Optional. The date in 'YYYY-MM-DD' format. If None, uses the latest available date.
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
Markdown table with industry classification data or an error message.
|
35
|
+
"""
|
36
|
+
log_msg = f"Tool 'get_stock_industry' called for code={code or 'all'}, date={date or 'latest'}"
|
37
|
+
logger.info(log_msg)
|
38
|
+
try:
|
39
|
+
# Add date validation if desired
|
40
|
+
df = active_data_source.get_stock_industry(code=code, date=date)
|
41
|
+
logger.info(
|
42
|
+
f"Successfully retrieved industry data for {code or 'all'}, {date or 'latest'}.")
|
43
|
+
from src.formatting.markdown_formatter import format_df_to_markdown
|
44
|
+
return format_df_to_markdown(df)
|
45
|
+
|
46
|
+
except Exception as e:
|
47
|
+
logger.exception(
|
48
|
+
f"Exception processing get_stock_industry: {e}")
|
49
|
+
return f"Error: An unexpected error occurred: {e}"
|
50
|
+
|
51
|
+
@app.tool()
|
52
|
+
def get_sz50_stocks(date: Optional[str] = None) -> str:
|
53
|
+
"""
|
54
|
+
Fetches the constituent stocks of the SZSE 50 Index for a given date.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
date: Optional. The date in 'YYYY-MM-DD' format. If None, uses the latest available date.
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
Markdown table with SZSE 50 constituent stocks or an error message.
|
61
|
+
"""
|
62
|
+
return call_index_constituent_tool(
|
63
|
+
"get_sz50_stocks",
|
64
|
+
active_data_source.get_sz50_stocks,
|
65
|
+
"SZSE 50",
|
66
|
+
date
|
67
|
+
)
|
68
|
+
|
69
|
+
@app.tool()
|
70
|
+
def get_hs300_stocks(date: Optional[str] = None) -> str:
|
71
|
+
"""
|
72
|
+
Fetches the constituent stocks of the CSI 300 Index for a given date.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
date: Optional. The date in 'YYYY-MM-DD' format. If None, uses the latest available date.
|
76
|
+
|
77
|
+
Returns:
|
78
|
+
Markdown table with CSI 300 constituent stocks or an error message.
|
79
|
+
"""
|
80
|
+
return call_index_constituent_tool(
|
81
|
+
"get_hs300_stocks",
|
82
|
+
active_data_source.get_hs300_stocks,
|
83
|
+
"CSI 300",
|
84
|
+
date
|
85
|
+
)
|
86
|
+
|
87
|
+
@app.tool()
|
88
|
+
def get_zz500_stocks(date: Optional[str] = None) -> str:
|
89
|
+
"""
|
90
|
+
Fetches the constituent stocks of the CSI 500 Index for a given date.
|
91
|
+
|
92
|
+
Args:
|
93
|
+
date: Optional. The date in 'YYYY-MM-DD' format. If None, uses the latest available date.
|
94
|
+
|
95
|
+
Returns:
|
96
|
+
Markdown table with CSI 500 constituent stocks or an error message.
|
97
|
+
"""
|
98
|
+
return call_index_constituent_tool(
|
99
|
+
"get_zz500_stocks",
|
100
|
+
active_data_source.get_zz500_stocks,
|
101
|
+
"CSI 500",
|
102
|
+
date
|
103
|
+
)
|
tools/macroeconomic.py
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
"""
|
2
|
+
Macroeconomic data tools for MCP server.
|
3
|
+
Contains tools for fetching interest rates, money supply data, and more.
|
4
|
+
"""
|
5
|
+
import logging
|
6
|
+
from typing import Optional
|
7
|
+
|
8
|
+
from mcp.server.fastmcp import FastMCP
|
9
|
+
from src.data_source_interface import FinancialDataSource
|
10
|
+
from src.tools.base import call_macro_data_tool
|
11
|
+
|
12
|
+
logger = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
def register_macroeconomic_tools(app: FastMCP, active_data_source: FinancialDataSource):
|
16
|
+
"""
|
17
|
+
Register macroeconomic data tools with the MCP app.
|
18
|
+
|
19
|
+
Args:
|
20
|
+
app: The FastMCP app instance
|
21
|
+
active_data_source: The active financial data source
|
22
|
+
"""
|
23
|
+
|
24
|
+
@app.tool()
|
25
|
+
def get_deposit_rate_data(start_date: Optional[str] = None, end_date: Optional[str] = None) -> str:
|
26
|
+
"""
|
27
|
+
Fetches benchmark deposit rates (活期, 定期) within a date range.
|
28
|
+
|
29
|
+
Args:
|
30
|
+
start_date: Optional. Start date in 'YYYY-MM-DD' format.
|
31
|
+
end_date: Optional. End date in 'YYYY-MM-DD' format.
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
Markdown table with deposit rate data or an error message.
|
35
|
+
"""
|
36
|
+
return call_macro_data_tool(
|
37
|
+
"get_deposit_rate_data",
|
38
|
+
active_data_source.get_deposit_rate_data,
|
39
|
+
"Deposit Rate",
|
40
|
+
start_date, end_date
|
41
|
+
)
|
42
|
+
|
43
|
+
@app.tool()
|
44
|
+
def get_loan_rate_data(start_date: Optional[str] = None, end_date: Optional[str] = None) -> str:
|
45
|
+
"""
|
46
|
+
Fetches benchmark loan rates (贷款利率) within a date range.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
start_date: Optional. Start date in 'YYYY-MM-DD' format.
|
50
|
+
end_date: Optional. End date in 'YYYY-MM-DD' format.
|
51
|
+
|
52
|
+
Returns:
|
53
|
+
Markdown table with loan rate data or an error message.
|
54
|
+
"""
|
55
|
+
return call_macro_data_tool(
|
56
|
+
"get_loan_rate_data",
|
57
|
+
active_data_source.get_loan_rate_data,
|
58
|
+
"Loan Rate",
|
59
|
+
start_date, end_date
|
60
|
+
)
|
61
|
+
|
62
|
+
@app.tool()
|
63
|
+
def get_required_reserve_ratio_data(start_date: Optional[str] = None, end_date: Optional[str] = None, year_type: str = '0') -> str:
|
64
|
+
"""
|
65
|
+
Fetches required reserve ratio data (存款准备金率) within a date range.
|
66
|
+
|
67
|
+
Args:
|
68
|
+
start_date: Optional. Start date in 'YYYY-MM-DD' format.
|
69
|
+
end_date: Optional. End date in 'YYYY-MM-DD' format.
|
70
|
+
year_type: Optional. Year type for date filtering. '0' for announcement date (公告日期, default),
|
71
|
+
'1' for effective date (生效日期).
|
72
|
+
|
73
|
+
Returns:
|
74
|
+
Markdown table with required reserve ratio data or an error message.
|
75
|
+
"""
|
76
|
+
# Basic validation for year_type
|
77
|
+
if year_type not in ['0', '1']:
|
78
|
+
logger.warning(f"Invalid year_type requested: {year_type}")
|
79
|
+
return "Error: Invalid year_type '{year_type}'. Valid options are '0' (announcement date) or '1' (effective date)."
|
80
|
+
|
81
|
+
return call_macro_data_tool(
|
82
|
+
"get_required_reserve_ratio_data",
|
83
|
+
active_data_source.get_required_reserve_ratio_data,
|
84
|
+
"Required Reserve Ratio",
|
85
|
+
start_date, end_date,
|
86
|
+
yearType=year_type # Pass the extra arg correctly named for Baostock
|
87
|
+
)
|
88
|
+
|
89
|
+
@app.tool()
|
90
|
+
def get_money_supply_data_month(start_date: Optional[str] = None, end_date: Optional[str] = None) -> str:
|
91
|
+
"""
|
92
|
+
Fetches monthly money supply data (M0, M1, M2) within a date range.
|
93
|
+
|
94
|
+
Args:
|
95
|
+
start_date: Optional. Start date in 'YYYY-MM' format.
|
96
|
+
end_date: Optional. End date in 'YYYY-MM' format.
|
97
|
+
|
98
|
+
Returns:
|
99
|
+
Markdown table with monthly money supply data or an error message.
|
100
|
+
"""
|
101
|
+
# Add specific validation for YYYY-MM format if desired
|
102
|
+
return call_macro_data_tool(
|
103
|
+
"get_money_supply_data_month",
|
104
|
+
active_data_source.get_money_supply_data_month,
|
105
|
+
"Monthly Money Supply",
|
106
|
+
start_date, end_date
|
107
|
+
)
|
108
|
+
|
109
|
+
@app.tool()
|
110
|
+
def get_money_supply_data_year(start_date: Optional[str] = None, end_date: Optional[str] = None) -> str:
|
111
|
+
"""
|
112
|
+
Fetches yearly money supply data (M0, M1, M2 - year end balance) within a date range.
|
113
|
+
|
114
|
+
Args:
|
115
|
+
start_date: Optional. Start year in 'YYYY' format.
|
116
|
+
end_date: Optional. End year in 'YYYY' format.
|
117
|
+
|
118
|
+
Returns:
|
119
|
+
Markdown table with yearly money supply data or an error message.
|
120
|
+
"""
|
121
|
+
# Add specific validation for YYYY format if desired
|
122
|
+
return call_macro_data_tool(
|
123
|
+
"get_money_supply_data_year",
|
124
|
+
active_data_source.get_money_supply_data_year,
|
125
|
+
"Yearly Money Supply",
|
126
|
+
start_date, end_date
|
127
|
+
)
|
128
|
+
|
129
|
+
@app.tool()
|
130
|
+
def get_shibor_data(start_date: Optional[str] = None, end_date: Optional[str] = None) -> str:
|
131
|
+
"""
|
132
|
+
Fetches SHIBOR (Shanghai Interbank Offered Rate) data within a date range.
|
133
|
+
|
134
|
+
Args:
|
135
|
+
start_date: Optional. Start date in 'YYYY-MM-DD' format.
|
136
|
+
end_date: Optional. End date in 'YYYY-MM-DD' format.
|
137
|
+
|
138
|
+
Returns:
|
139
|
+
Markdown table with SHIBOR data or an error message.
|
140
|
+
"""
|
141
|
+
return call_macro_data_tool(
|
142
|
+
"get_shibor_data",
|
143
|
+
active_data_source.get_shibor_data,
|
144
|
+
"SHIBOR",
|
145
|
+
start_date, end_date
|
146
|
+
)
|