guohanghui commited on
Commit
c659bdf
·
verified ·
1 Parent(s): 443598c

Update qlib/mcp_output/mcp_plugin/mcp_service.py

Browse files
qlib/mcp_output/mcp_plugin/mcp_service.py CHANGED
@@ -7,33 +7,9 @@ import qlib
7
  data_dir = os.getenv("QLIB_DATA_DIR", "/app/qlib_data")
8
  print(f"Using Qlib data path: {data_dir}")
9
 
 
10
  qlib.init(provider_uri=data_dir, region="cn")
11
 
12
- # 根据你的数据包实际目录结构调整
13
- instruments_path = os.path.join(data_dir, "instruments") # 或 "cn_data/instruments"
14
- calendars_path = os.path.join(data_dir, "calendars")
15
- features_path = os.path.join(data_dir, "features")
16
-
17
- # 打印 instruments
18
- if os.path.exists(instruments_path):
19
- print("\n=== Instruments directory ===")
20
- for freq in os.listdir(instruments_path):
21
- freq_path = os.path.join(instruments_path, freq)
22
- if os.path.isdir(freq_path):
23
- files = os.listdir(freq_path)
24
- print(f"{freq}/ : {len(files)} files -> {files[:10]}{'...' if len(files) > 10 else ''}")
25
- else:
26
- print("Instruments directory not found!")
27
-
28
- # 尝试列出 day 级标的
29
- try:
30
- instruments = D.list_instruments(freq="day", market="cn")
31
- print(f"\nNumber of day-frequency instruments: {len(instruments)}")
32
- print(f"Sample instruments: {instruments[:10]}")
33
- except Exception as e:
34
- print("Error listing day-frequency instruments:", e)
35
-
36
-
37
  # Path settings
38
  source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
39
  sys.path.insert(0, source_path)
@@ -55,13 +31,13 @@ from qlib.workflow.task.manage import TaskManager
55
  mcp = FastMCP("qlib_service")
56
 
57
 
58
- @mcp.tool(name="initialize_exchange", description="Initialize and return an Exchange object for backtesting or trading simulations.")
59
  def initialize_exchange(
60
  exchange: Optional[str] = None,
61
- freq: str = "day",
62
  start_time: Optional[str] = None,
63
  end_time: Optional[str] = None,
64
- codes: str = "all",
65
  subscribe_fields: Optional[List[str]] = None,
66
  open_cost: float = 0.0015,
67
  close_cost: float = 0.0025,
@@ -72,23 +48,32 @@ def initialize_exchange(
72
  ) -> dict:
73
  """
74
  Initialize and return an Exchange object for backtesting or trading simulations.
75
-
76
  Parameters:
77
  exchange (Optional[str]): Existing exchange name or configuration.
78
- freq (str): Frequency of data (e.g., 'day', 'minute').
79
  start_time (Optional[str]): Start time for the exchange (e.g., '2020-01-01').
80
  end_time (Optional[str]): End time for the exchange (e.g., '2021-01-01').
81
- codes (str): Instruments string (e.g., 'all', 'csi500').
82
- subscribe_fields (Optional[List[str]]): Data fields to subscribe to.
83
- open_cost (float): Open transaction cost as a ratio.
84
- close_cost (float): Close transaction cost as a ratio.
85
- min_cost (float): Minimum transaction cost.
86
- limit_threshold (Optional[float]): Price movement limits.
87
- deal_price (Optional[str]): Price configuration.
88
  extra_kwargs (Optional[Dict[str, Any]]): Additional keyword arguments as a dictionary.
89
-
90
  Returns:
91
- dict: A dictionary containing success, result, or error fields.
 
 
 
 
 
 
 
 
 
92
  """
93
  try:
94
  # Handle None defaults
@@ -97,6 +82,14 @@ def initialize_exchange(
97
  if extra_kwargs is None:
98
  extra_kwargs = {}
99
 
 
 
 
 
 
 
 
 
100
  exchange_obj = get_exchange(
101
  exchange=exchange,
102
  freq=freq,
@@ -111,7 +104,11 @@ def initialize_exchange(
111
  deal_price=deal_price,
112
  **extra_kwargs,
113
  )
114
- return {"success": True, "result": str(exchange_obj), "message": "Exchange initialized successfully"}
 
 
 
 
115
  except Exception as e:
116
  return {"success": False, "error": str(e)}
117
 
@@ -126,16 +123,24 @@ def create_account(
126
  ) -> dict:
127
  """
128
  Create and initialize an Account instance for trading simulations.
129
-
130
  Parameters:
131
  start_time (str): Start time of the benchmark (e.g., '2020-01-01').
132
  end_time (str): End time of the benchmark (e.g., '2021-01-01').
133
- benchmark (str): Benchmark for reporting.
134
- account (float): Initial cash amount.
135
  pos_type (str): Type of position to use (default: "Position").
136
-
137
  Returns:
138
- dict: A dictionary containing success, result, or error fields.
 
 
 
 
 
 
 
 
139
  """
140
  try:
141
  account_obj = create_account_instance(
@@ -145,7 +150,11 @@ def create_account(
145
  account=account,
146
  pos_type=pos_type,
147
  )
148
- return {"success": True, "result": str(account_obj), "message": "Account created successfully"}
 
 
 
 
149
  except Exception as e:
150
  return {"success": False, "error": str(e)}
151
 
@@ -163,19 +172,22 @@ def initialize_strategy_executor(
163
  ) -> dict:
164
  """
165
  Initialize and configure a trading strategy and its executor.
166
-
167
  Parameters:
168
  start_time (str): Start time for the strategy (e.g., '2020-01-01').
169
  end_time (str): End time for the strategy (e.g., '2021-01-01').
170
- strategy (dict): Strategy configuration.
 
171
  executor (dict): Executor configuration.
172
- benchmark (str): Benchmark identifier.
173
- account (float): Initial cash amount.
174
- exchange_kwargs (dict): Exchange-specific settings.
 
 
175
  pos_type (str): Type of position to use (default: "Position").
176
-
177
  Returns:
178
- dict: A dictionary containing success, result, or error fields.
179
  """
180
  try:
181
  strategy_executor = get_strategy_executor(
@@ -188,7 +200,11 @@ def initialize_strategy_executor(
188
  exchange_kwargs=exchange_kwargs,
189
  pos_type=pos_type,
190
  )
191
- return {"success": True, "result": str(strategy_executor), "message": "Strategy executor initialized successfully"}
 
 
 
 
192
  except Exception as e:
193
  return {"success": False, "error": str(e)}
194
 
@@ -206,21 +222,39 @@ def run_backtest(
206
  ) -> dict:
207
  """
208
  Perform a backtest to evaluate a trading strategy.
209
-
210
  Parameters:
211
  start_time (str): Start time for the backtest (e.g., '2020-01-01').
212
  end_time (str): End time for the backtest (e.g., '2021-01-01').
213
  strategy (dict): Strategy configuration.
 
214
  executor (dict): Executor configuration.
215
- benchmark (str): Benchmark identifier.
216
- account (float): Initial cash amount.
217
- exchange_kwargs (dict): Exchange-specific settings.
 
 
218
  pos_type (str): Type of position to use (default: "Position").
219
-
220
  Returns:
221
- dict: A dictionary containing success, result, or error fields.
 
 
 
 
 
 
 
 
 
 
 
222
  """
223
  try:
 
 
 
 
224
  portfolio_metrics, trading_indicators = backtest(
225
  start_time=start_time,
226
  end_time=end_time,
@@ -238,7 +272,11 @@ def run_backtest(
238
  "trading_indicators": trading_indicators.to_dict() if hasattr(trading_indicators, 'to_dict') else str(trading_indicators)
239
  }
240
 
241
- return {"success": True, "result": result, "message": "Backtest completed successfully"}
 
 
 
 
242
  except Exception as e:
243
  return {"success": False, "error": str(e)}
244
 
@@ -256,7 +294,7 @@ def collect_trade_data(
256
  ) -> dict:
257
  """
258
  Collect trade decision data for reinforcement learning training.
259
-
260
  Parameters:
261
  start_time (str): Start time for data collection (e.g., '2020-01-01').
262
  end_time (str): End time for data collection (e.g., '2021-01-01').
@@ -264,13 +302,17 @@ def collect_trade_data(
264
  executor (dict): Executor configuration.
265
  benchmark (str): Benchmark identifier.
266
  account (float): Initial cash amount.
267
- exchange_kwargs (dict): Exchange-specific settings.
268
  pos_type (str): Type of position to use (default: "Position").
269
-
270
  Returns:
271
- dict: A dictionary containing success, result, or error fields.
272
  """
273
  try:
 
 
 
 
274
  data_generator = collect_data(
275
  start_time=start_time,
276
  end_time=end_time,
@@ -283,7 +325,11 @@ def collect_trade_data(
283
  return_value=None,
284
  )
285
  data = list(data_generator)
286
- return {"success": True, "result": [str(item) for item in data], "message": "Trade data collected successfully"}
 
 
 
 
287
  except Exception as e:
288
  return {"success": False, "error": str(e)}
289
 
@@ -292,16 +338,64 @@ def collect_trade_data(
292
  def format_trade_decisions(decisions: List[str]) -> dict:
293
  """
294
  Format trade decisions into a hierarchical structure.
295
-
296
  Parameters:
297
  decisions (List[str]): List of trade decisions as strings.
298
-
299
  Returns:
300
- dict: A dictionary containing success, result, or error fields.
301
  """
302
  try:
303
  formatted_decisions = format_decisions(decisions)
304
- return {"success": True, "result": str(formatted_decisions), "message": "Decisions formatted successfully"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
305
  except Exception as e:
306
  return {"success": False, "error": str(e)}
307
 
@@ -309,7 +403,7 @@ def format_trade_decisions(decisions: List[str]) -> dict:
309
  def create_app() -> FastMCP:
310
  """
311
  Create and return the FastMCP application instance.
312
-
313
  Returns:
314
  FastMCP: The FastMCP application instance.
315
  """
 
7
  data_dir = os.getenv("QLIB_DATA_DIR", "/app/qlib_data")
8
  print(f"Using Qlib data path: {data_dir}")
9
 
10
+ # 初始化 qlib,使用 day_future 作为默认频率
11
  qlib.init(provider_uri=data_dir, region="cn")
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  # Path settings
14
  source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
15
  sys.path.insert(0, source_path)
 
31
  mcp = FastMCP("qlib_service")
32
 
33
 
34
+ @mcp.tool(name="initialize_exchange", description="Initialize and return an Exchange object for backtesting or trading simulations. Uses day_future frequency by default to match available data.")
35
  def initialize_exchange(
36
  exchange: Optional[str] = None,
37
+ freq: str = "day_future", # 修改默认值为 day_future
38
  start_time: Optional[str] = None,
39
  end_time: Optional[str] = None,
40
+ codes: str = "csi300", # 修改默认值为 csi300(更常用)
41
  subscribe_fields: Optional[List[str]] = None,
42
  open_cost: float = 0.0015,
43
  close_cost: float = 0.0025,
 
48
  ) -> dict:
49
  """
50
  Initialize and return an Exchange object for backtesting or trading simulations.
51
+
52
  Parameters:
53
  exchange (Optional[str]): Existing exchange name or configuration.
54
+ freq (str): Frequency of data. Default is 'day_future' (available: 'day_future').
55
  start_time (Optional[str]): Start time for the exchange (e.g., '2020-01-01').
56
  end_time (Optional[str]): End time for the exchange (e.g., '2021-01-01').
57
+ codes (str): Instruments string. Available options: 'all', 'csi300', 'csi500', 'csi800', 'csi1000', 'csiall'. Default: 'csi300'.
58
+ subscribe_fields (Optional[List[str]]): Data fields to subscribe to (e.g., ['$close', '$volume', '$open', '$high', '$low']).
59
+ open_cost (float): Open transaction cost as a ratio (default: 0.0015 = 0.15%).
60
+ close_cost (float): Close transaction cost as a ratio (default: 0.0025 = 0.25%).
61
+ min_cost (float): Minimum transaction cost in currency units (default: 5.0).
62
+ limit_threshold (Optional[float]): Price movement limits (e.g., 0.095 for 9.5% limit).
63
+ deal_price (Optional[str]): Price configuration for order execution.
64
  extra_kwargs (Optional[Dict[str, Any]]): Additional keyword arguments as a dictionary.
65
+
66
  Returns:
67
+ dict: A dictionary containing success status, result, or error information.
68
+
69
+ Example:
70
+ initialize_exchange(
71
+ freq="day_future",
72
+ start_time="2020-01-01",
73
+ end_time="2021-12-31",
74
+ codes="csi300",
75
+ subscribe_fields=["$close", "$volume"]
76
+ )
77
  """
78
  try:
79
  # Handle None defaults
 
82
  if extra_kwargs is None:
83
  extra_kwargs = {}
84
 
85
+ # 验证 codes 参数
86
+ valid_codes = ['all', 'csi300', 'csi500', 'csi800', 'csi1000', 'csiall']
87
+ if codes not in valid_codes:
88
+ return {
89
+ "success": False,
90
+ "error": f"Invalid codes parameter. Available options: {', '.join(valid_codes)}"
91
+ }
92
+
93
  exchange_obj = get_exchange(
94
  exchange=exchange,
95
  freq=freq,
 
104
  deal_price=deal_price,
105
  **extra_kwargs,
106
  )
107
+ return {
108
+ "success": True,
109
+ "result": str(exchange_obj),
110
+ "message": f"Exchange initialized successfully with freq={freq}, codes={codes}"
111
+ }
112
  except Exception as e:
113
  return {"success": False, "error": str(e)}
114
 
 
123
  ) -> dict:
124
  """
125
  Create and initialize an Account instance for trading simulations.
126
+
127
  Parameters:
128
  start_time (str): Start time of the benchmark (e.g., '2020-01-01').
129
  end_time (str): End time of the benchmark (e.g., '2021-01-01').
130
+ benchmark (str): Benchmark for performance comparison (e.g., 'SH000300' for CSI300, 'SH000905' for CSI500).
131
+ account (float): Initial cash amount (e.g., 1000000 for 1 million).
132
  pos_type (str): Type of position to use (default: "Position").
133
+
134
  Returns:
135
+ dict: A dictionary containing success status, result, or error information.
136
+
137
+ Example:
138
+ create_account(
139
+ start_time="2020-01-01",
140
+ end_time="2021-12-31",
141
+ benchmark="SH000300",
142
+ account=1000000.0
143
+ )
144
  """
145
  try:
146
  account_obj = create_account_instance(
 
150
  account=account,
151
  pos_type=pos_type,
152
  )
153
+ return {
154
+ "success": True,
155
+ "result": str(account_obj),
156
+ "message": f"Account created successfully with initial capital: {account}"
157
+ }
158
  except Exception as e:
159
  return {"success": False, "error": str(e)}
160
 
 
172
  ) -> dict:
173
  """
174
  Initialize and configure a trading strategy and its executor.
175
+
176
  Parameters:
177
  start_time (str): Start time for the strategy (e.g., '2020-01-01').
178
  end_time (str): End time for the strategy (e.g., '2021-01-01').
179
+ strategy (dict): Strategy configuration with class and module info.
180
+ Example: {"class": "TopkDropoutStrategy", "module_path": "qlib.contrib.strategy", "kwargs": {"topk": 30, "n_drop": 5}}
181
  executor (dict): Executor configuration.
182
+ Example: {"class": "SimulatorExecutor", "module_path": "qlib.backtest.executor", "kwargs": {"time_per_step": "day"}}
183
+ benchmark (str): Benchmark identifier (e.g., 'SH000300' for CSI300).
184
+ account (float): Initial cash amount (e.g., 1000000).
185
+ exchange_kwargs (dict): Exchange-specific settings. Should include 'freq' and 'codes'.
186
+ Example: {"freq": "day_future", "codes": "csi300", "start_time": "2020-01-01", "end_time": "2021-12-31"}
187
  pos_type (str): Type of position to use (default: "Position").
188
+
189
  Returns:
190
+ dict: A dictionary containing success status, result, or error information.
191
  """
192
  try:
193
  strategy_executor = get_strategy_executor(
 
200
  exchange_kwargs=exchange_kwargs,
201
  pos_type=pos_type,
202
  )
203
+ return {
204
+ "success": True,
205
+ "result": str(strategy_executor),
206
+ "message": "Strategy executor initialized successfully"
207
+ }
208
  except Exception as e:
209
  return {"success": False, "error": str(e)}
210
 
 
222
  ) -> dict:
223
  """
224
  Perform a backtest to evaluate a trading strategy.
225
+
226
  Parameters:
227
  start_time (str): Start time for the backtest (e.g., '2020-01-01').
228
  end_time (str): End time for the backtest (e.g., '2021-01-01').
229
  strategy (dict): Strategy configuration.
230
+ Example: {"class": "TopkDropoutStrategy", "module_path": "qlib.contrib.strategy", "kwargs": {"topk": 30, "n_drop": 5}}
231
  executor (dict): Executor configuration.
232
+ Example: {"class": "SimulatorExecutor", "module_path": "qlib.backtest.executor", "kwargs": {"time_per_step": "day"}}
233
+ benchmark (str): Benchmark identifier (e.g., 'SH000300' for CSI300, 'SH000905' for CSI500).
234
+ account (float): Initial cash amount (e.g., 1000000).
235
+ exchange_kwargs (dict): Exchange-specific settings. Must include 'freq' as 'day_future' and valid 'codes'.
236
+ Example: {"freq": "day_future", "codes": "csi300", "open_cost": 0.0015, "close_cost": 0.0025, "min_cost": 5}
237
  pos_type (str): Type of position to use (default: "Position").
238
+
239
  Returns:
240
+ dict: A dictionary containing success status, portfolio metrics, trading indicators, or error information.
241
+
242
+ Example:
243
+ run_backtest(
244
+ start_time="2020-01-01",
245
+ end_time="2021-12-31",
246
+ strategy={"class": "TopkDropoutStrategy", "module_path": "qlib.contrib.strategy", "kwargs": {"topk": 30}},
247
+ executor={"class": "SimulatorExecutor", "module_path": "qlib.backtest.executor"},
248
+ benchmark="SH000300",
249
+ account=1000000,
250
+ exchange_kwargs={"freq": "day_future", "codes": "csi300"}
251
+ )
252
  """
253
  try:
254
+ # 确保 exchange_kwargs 中包含正确的 freq
255
+ if 'freq' not in exchange_kwargs:
256
+ exchange_kwargs['freq'] = 'day_future'
257
+
258
  portfolio_metrics, trading_indicators = backtest(
259
  start_time=start_time,
260
  end_time=end_time,
 
272
  "trading_indicators": trading_indicators.to_dict() if hasattr(trading_indicators, 'to_dict') else str(trading_indicators)
273
  }
274
 
275
+ return {
276
+ "success": True,
277
+ "result": result,
278
+ "message": "Backtest completed successfully"
279
+ }
280
  except Exception as e:
281
  return {"success": False, "error": str(e)}
282
 
 
294
  ) -> dict:
295
  """
296
  Collect trade decision data for reinforcement learning training.
297
+
298
  Parameters:
299
  start_time (str): Start time for data collection (e.g., '2020-01-01').
300
  end_time (str): End time for data collection (e.g., '2021-01-01').
 
302
  executor (dict): Executor configuration.
303
  benchmark (str): Benchmark identifier.
304
  account (float): Initial cash amount.
305
+ exchange_kwargs (dict): Exchange-specific settings. Should use 'freq': 'day_future'.
306
  pos_type (str): Type of position to use (default: "Position").
307
+
308
  Returns:
309
+ dict: A dictionary containing success status, collected data, or error information.
310
  """
311
  try:
312
+ # 确保使用正确的 freq
313
+ if 'freq' not in exchange_kwargs:
314
+ exchange_kwargs['freq'] = 'day_future'
315
+
316
  data_generator = collect_data(
317
  start_time=start_time,
318
  end_time=end_time,
 
325
  return_value=None,
326
  )
327
  data = list(data_generator)
328
+ return {
329
+ "success": True,
330
+ "result": [str(item) for item in data],
331
+ "message": f"Trade data collected successfully. Total records: {len(data)}"
332
+ }
333
  except Exception as e:
334
  return {"success": False, "error": str(e)}
335
 
 
338
  def format_trade_decisions(decisions: List[str]) -> dict:
339
  """
340
  Format trade decisions into a hierarchical structure.
341
+
342
  Parameters:
343
  decisions (List[str]): List of trade decisions as strings.
344
+
345
  Returns:
346
+ dict: A dictionary containing success status, formatted decisions, or error information.
347
  """
348
  try:
349
  formatted_decisions = format_decisions(decisions)
350
+ return {
351
+ "success": True,
352
+ "result": str(formatted_decisions),
353
+ "message": "Decisions formatted successfully"
354
+ }
355
+ except Exception as e:
356
+ return {"success": False, "error": str(e)}
357
+
358
+
359
+ @mcp.tool(name="get_available_instruments", description="Get information about available instrument pools in the dataset.")
360
+ def get_available_instruments() -> dict:
361
+ """
362
+ Get information about available instrument pools in the dataset.
363
+
364
+ Returns:
365
+ dict: A dictionary containing available instrument pools and their descriptions.
366
+ """
367
+ try:
368
+ instruments_info = {
369
+ "available_pools": [
370
+ "all",
371
+ "csi300", # 沪深300指数成分股
372
+ "csi500", # 中证500指数成分股
373
+ "csi800", # 中证800指数成分股
374
+ "csi1000", # 中证1000指数成分股
375
+ "csiall" # 所有中证指数成分股
376
+ ],
377
+ "descriptions": {
378
+ "all": "All available stocks in the dataset",
379
+ "csi300": "CSI 300 Index constituents (top 300 stocks by market cap)",
380
+ "csi500": "CSI 500 Index constituents (mid-cap stocks)",
381
+ "csi800": "CSI 800 Index constituents (CSI 300 + CSI 500)",
382
+ "csi1000": "CSI 1000 Index constituents (small-cap stocks)",
383
+ "csiall": "All CSI Index constituents"
384
+ },
385
+ "benchmarks": {
386
+ "csi300": "SH000300",
387
+ "csi500": "SH000905",
388
+ "csi800": "SH000906",
389
+ "csi1000": "SH000852"
390
+ },
391
+ "frequency": "day_future",
392
+ "data_directory": data_dir
393
+ }
394
+ return {
395
+ "success": True,
396
+ "result": instruments_info,
397
+ "message": "Available instruments information retrieved successfully"
398
+ }
399
  except Exception as e:
400
  return {"success": False, "error": str(e)}
401
 
 
403
  def create_app() -> FastMCP:
404
  """
405
  Create and return the FastMCP application instance.
406
+
407
  Returns:
408
  FastMCP: The FastMCP application instance.
409
  """