cnhkmcp 1.2.0__tar.gz → 1.2.2__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cnhkmcp
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: A comprehensive Model Context Protocol (MCP) server for quantitative trading platform integration
5
5
  Home-page: https://github.com/cnhk/cnhkmcp
6
6
  Author: CNHK
@@ -50,7 +50,7 @@ from .untracked.forum_functions import (
50
50
  read_full_forum_post
51
51
  )
52
52
 
53
- __version__ = "1.2.0"
53
+ __version__ = "1.2.2"
54
54
  __author__ = "CNHK"
55
55
  __email__ = "cnhk@example.com"
56
56
 
@@ -389,7 +389,7 @@ class BrainApiClient:
389
389
  response = self.session.get(f"{self.base_url}/data-sets", params=params)
390
390
  response.raise_for_status()
391
391
  response = response.json()
392
- response['extraNote'] = "if your returned result is 0, you may want to check your parameter by using get_instrument_options tool to got correct parameter"
392
+ response['extraNote'] = "if your returned result is 0, you may want to check your parameter by using get_platform_setting_options tool to got correct parameter"
393
393
  return response
394
394
  except Exception as e:
395
395
  self.log(f"Failed to get datasets: {str(e)}", "ERROR")
@@ -423,7 +423,7 @@ class BrainApiClient:
423
423
  response = self.session.get(f"{self.base_url}/data-fields", params=params)
424
424
  response.raise_for_status()
425
425
  response = response.json()
426
- response['extraNote'] = "if your returned result is 0, you may want to check your parameter by using get_instrument_options tool to got correct parameter"
426
+ response['extraNote'] = "if your returned result is 0, you may want to check your parameter by using get_platform_setting_options tool to got correct parameter"
427
427
  return response
428
428
  except Exception as e:
429
429
  self.log(f"Failed to get datafields: {str(e)}", "ERROR")
@@ -436,7 +436,15 @@ class BrainApiClient:
436
436
  try:
437
437
  response = self.session.get(f"{self.base_url}/alphas/{alpha_id}/recordsets/pnl")
438
438
  response.raise_for_status()
439
- return response.json()
439
+ # Some alphas may return 204 No Content or an empty body
440
+ text = (response.text or "").strip()
441
+ if not text:
442
+ return {}
443
+ try:
444
+ return response.json()
445
+ except Exception as parse_err:
446
+ self.log(f"PnL JSON parse failed for {alpha_id}: {parse_err}", "WARNING")
447
+ return {}
440
448
  except Exception as e:
441
449
  self.log(f"Failed to get alpha PnL: {str(e)}", "ERROR")
442
450
  raise
@@ -786,9 +794,11 @@ class BrainApiClient:
786
794
  await self.ensure_authenticated()
787
795
 
788
796
  try:
789
- response = self.session.get(f"{self.base_url}/alphas/{alpha_id}/recordsets/production-correlation")
797
+ response = self.session.get(f"{self.base_url}/alphas/{alpha_id}/correlations/prod")
790
798
  response.raise_for_status()
791
- return response.json()
799
+ if response.text:
800
+ return response.json()
801
+ return {} # Return empty dict for empty response
792
802
  except Exception as e:
793
803
  self.log(f"Failed to get production correlation: {str(e)}", "ERROR")
794
804
  raise
@@ -798,9 +808,11 @@ class BrainApiClient:
798
808
  await self.ensure_authenticated()
799
809
 
800
810
  try:
801
- response = self.session.get(f"{self.base_url}/alphas/{alpha_id}/recordsets/self-correlation")
811
+ response = self.session.get(f"{self.base_url}/alphas/{alpha_id}/correlations/self")
802
812
  response.raise_for_status()
803
- return response.json()
813
+ if response.text:
814
+ return response.json()
815
+ return {} # Return empty dict for empty response
804
816
  except Exception as e:
805
817
  self.log(f"Failed to get self correlation: {str(e)}", "ERROR")
806
818
  raise
@@ -1050,7 +1062,7 @@ class BrainApiClient:
1050
1062
  self.log(f"Failed to get competition agreement: {str(e)}", "ERROR")
1051
1063
  raise
1052
1064
 
1053
- async def get_instrument_options(self) -> Dict[str, Any]:
1065
+ async def get_platform_setting_options(self) -> Dict[str, Any]:
1054
1066
  """Get available instrument types, regions, delays, and universes."""
1055
1067
  await self.ensure_authenticated()
1056
1068
 
@@ -1200,14 +1212,54 @@ brain_client = BrainApiClient()
1200
1212
  # Configuration management
1201
1213
  CONFIG_FILE = "user_config.json"
1202
1214
 
1215
+ def _resolve_config_path(for_write: bool = False) -> str:
1216
+ """Resolve the absolute path to the config file.
1217
+
1218
+ Resolution order:
1219
+ 1) BRAIN_CONFIG_PATH env var (explicit file path)
1220
+ 2) File in the same directory as this Python module
1221
+ 3) Current working directory (read fallback only)
1222
+
1223
+ When for_write is True, prefers module directory (or env path) and will
1224
+ return that even if it doesn't exist yet.
1225
+ """
1226
+ try:
1227
+ # 1) Explicit override
1228
+ env_path = os.environ.get("BRAIN_CONFIG_PATH")
1229
+ if env_path:
1230
+ return os.path.abspath(env_path)
1231
+
1232
+ # 2) Same directory as this .py module
1233
+ module_dir = os.path.dirname(os.path.abspath(__file__))
1234
+ module_path = os.path.join(module_dir, CONFIG_FILE)
1235
+ if not for_write and os.path.exists(module_path):
1236
+ return module_path
1237
+
1238
+ # 3) Fallback to CWD for backward compatibility (read-only preference)
1239
+ cwd_path = os.path.abspath(CONFIG_FILE)
1240
+ if not for_write and os.path.exists(cwd_path):
1241
+ return cwd_path
1242
+
1243
+ # For writes (or when nothing exists), prefer the module directory
1244
+ return module_path
1245
+ except Exception as e:
1246
+ # As a last resort, use the bare filename in CWD
1247
+ logger.error(f"Failed to resolve config path, defaulting to CWD: {e}")
1248
+ return os.path.abspath(CONFIG_FILE)
1249
+
1203
1250
  def load_config() -> Dict[str, Any]:
1204
- """Load configuration from file."""
1205
- if os.path.exists(CONFIG_FILE):
1251
+ """Load configuration from file with robust path resolution.
1252
+
1253
+ Looks for the config in this order: BRAIN_CONFIG_PATH -> module directory -> CWD.
1254
+ Returns an empty dict when not found or on error.
1255
+ """
1256
+ path = _resolve_config_path(for_write=False)
1257
+ if os.path.exists(path):
1206
1258
  try:
1207
- with open(CONFIG_FILE, 'r') as f:
1259
+ with open(path, 'r', encoding='utf-8') as f:
1208
1260
  return json.load(f)
1209
1261
  except Exception as e:
1210
- logger.error(f"Failed to load config: {e}")
1262
+ logger.error(f"Failed to load config from '{path}': {e}")
1211
1263
  return {}
1212
1264
 
1213
1265
  def load_brain_credentials() -> Optional[tuple]:
@@ -1225,10 +1277,16 @@ def load_brain_credentials() -> Optional[tuple]:
1225
1277
  return None
1226
1278
 
1227
1279
  def save_config(config: Dict[str, Any]):
1228
- """Save configuration to file."""
1280
+ """Save configuration to file using the resolved config path.
1281
+
1282
+ Uses BRAIN_CONFIG_PATH if set; otherwise writes next to this module.
1283
+ Ensures the target directory exists.
1284
+ """
1229
1285
  try:
1230
- with open(CONFIG_FILE, 'w') as f:
1231
- json.dump(config, f, indent=2)
1286
+ path = _resolve_config_path(for_write=True)
1287
+ os.makedirs(os.path.dirname(path), exist_ok=True)
1288
+ with open(path, 'w', encoding='utf-8') as f:
1289
+ json.dump(config, f, indent=2, ensure_ascii=False)
1232
1290
  except Exception as e:
1233
1291
  logger.error(f"Failed to save config: {e}")
1234
1292
 
@@ -1636,35 +1694,7 @@ async def save_simulation_data(simulation_id: str, filename: str) -> Dict[str, A
1636
1694
  except Exception as e:
1637
1695
  return {"error": str(e)}
1638
1696
 
1639
- @mcp.tool()
1640
- async def analyze_alpha_performance(alpha_id: str) -> Dict[str, Any]:
1641
- """
1642
- 📊 Comprehensive alpha performance analysis.
1643
-
1644
- Args:
1645
- alpha_id: The alpha ID to analyze
1646
-
1647
- Returns:
1648
- Comprehensive performance analysis
1649
- """
1650
- try:
1651
- # Get alpha details
1652
- alpha_details = await brain_client.get_alpha_details(alpha_id)
1653
-
1654
- # Get PnL data
1655
- pnl_data = await brain_client.get_alpha_pnl(alpha_id)
1656
-
1657
- # Get yearly stats if available
1658
- yearly_stats = await brain_client.get_alpha_yearly_stats(alpha_id)
1659
-
1660
- return {
1661
- "alpha_details": alpha_details,
1662
- "pnl_data": pnl_data,
1663
- "yearly_stats": yearly_stats,
1664
- "analysis_timestamp": datetime.now().isoformat()
1665
- }
1666
- except Exception as e:
1667
- return {"error": str(e)}
1697
+
1668
1698
 
1669
1699
  @mcp.tool()
1670
1700
  async def get_operators() -> Dict[str, Any]:
@@ -1962,10 +1992,18 @@ async def get_competition_agreement(competition_id: str) -> Dict[str, Any]:
1962
1992
  return {"error": str(e)}
1963
1993
 
1964
1994
  @mcp.tool()
1965
- async def get_instrument_options() -> Dict[str, Any]:
1966
- """Get available instrument types, regions, delays, and universes."""
1995
+ async def get_platform_setting_options() -> Dict[str, Any]:
1996
+ """Discover valid simulation setting options (instrument types, regions, delays, universes, neutralization).
1997
+
1998
+ Use this when a simulation request might contain an invalid/mismatched setting. If an AI or user supplies
1999
+ incorrect parameters (e.g., wrong region for an instrument type), call this tool to retrieve the authoritative
2000
+ option sets and correct the inputs before proceeding.
2001
+
2002
+ Returns:
2003
+ A structured list of valid combinations and choice lists to validate or fix simulation settings.
2004
+ """
1967
2005
  try:
1968
- return await brain_client.get_instrument_options()
2006
+ return await brain_client.get_platform_setting_options()
1969
2007
  except Exception as e:
1970
2008
  return {"error": str(e)}
1971
2009
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cnhkmcp
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: A comprehensive Model Context Protocol (MCP) server for quantitative trading platform integration
5
5
  Home-page: https://github.com/cnhk/cnhkmcp
6
6
  Author: CNHK
@@ -13,7 +13,7 @@ def read_requirements():
13
13
 
14
14
  setup(
15
15
  name="cnhkmcp",
16
- version="1.2.0",
16
+ version="1.2.2",
17
17
  author="CNHK",
18
18
  author_email="cnhk@example.com",
19
19
  description="A comprehensive Model Context Protocol (MCP) server for quantitative trading platform integration",
File without changes
File without changes
File without changes
File without changes
File without changes