cnhkmcp 1.3.7__tar.gz → 1.4.0__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.
Files changed (76) hide show
  1. {cnhkmcp-1.3.7/cnhkmcp.egg-info → cnhkmcp-1.4.0}/PKG-INFO +1 -1
  2. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/__init__.py +1 -1
  3. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/hkSimulator/autosimulator.py +33 -1
  4. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/static/script.js +106 -4
  5. cnhkmcp-1.4.0/cnhkmcp/untracked/__pycache__/forum_functions.cpython-313.pyc +0 -0
  6. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/platform_functions.py +167 -4
  7. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_daily_report_workflow.md +2 -0
  8. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0/cnhkmcp.egg-info}/PKG-INFO +1 -1
  9. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp.egg-info/SOURCES.txt +2 -1
  10. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/setup.py +1 -1
  11. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/LICENSE +0 -0
  12. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/MANIFEST.in +0 -0
  13. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/README.md +0 -0
  14. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/.gitignore +0 -0
  15. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/MODULAR_STRUCTURE.md +0 -0
  16. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/README.md +0 -0
  17. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/__pycache__/app.cpython-313.pyc +0 -0
  18. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/blueprints/__init__.py +0 -0
  19. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/blueprints/__pycache__/__init__.cpython-313.pyc +0 -0
  20. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/blueprints/__pycache__/feature_engineering.cpython-313.pyc +0 -0
  21. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/blueprints/__pycache__/idea_house.cpython-313.pyc +0 -0
  22. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/blueprints/__pycache__/inspiration_house.cpython-313.pyc +0 -0
  23. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/blueprints/__pycache__/paper_analysis.cpython-313.pyc +0 -0
  24. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/blueprints/__pycache__/simulator.cpython-313.pyc +0 -0
  25. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/blueprints/__pycache__/unified_tools.cpython-313.pyc +0 -0
  26. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/blueprints/__pycache__/wqb_simulator.cpython-313.pyc +0 -0
  27. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/blueprints/feature_engineering.py +0 -0
  28. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/blueprints/idea_house.py +0 -0
  29. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/blueprints/inspiration_house.py +0 -0
  30. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/blueprints/paper_analysis.py +0 -0
  31. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/custom_templates/templates.json +0 -0
  32. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/hkSimulator/ace_lib.py +0 -0
  33. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/hkSimulator/helpful_functions.py +0 -0
  34. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/mirror_config.txt +0 -0
  35. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/operaters.csv +0 -0
  36. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/requirements.txt +0 -0
  37. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/run_app.bat +0 -0
  38. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/run_app.sh +0 -0
  39. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/setup_tsinghua.bat +0 -0
  40. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/setup_tsinghua.sh +0 -0
  41. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/simulator/__pycache__/simulator_wqb.cpython-313.pyc +0 -0
  42. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/simulator/alpha_submitter.py +0 -0
  43. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/simulator/simulator_wqb.py +0 -0
  44. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/ssrn-3332513.pdf +0 -0
  45. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/static/brain.js +0 -0
  46. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/static/decoder.js +0 -0
  47. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/static/feature_engineering.js +0 -0
  48. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/static/idea_house.js +0 -0
  49. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/static/inspiration_house.js +0 -0
  50. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/static/paper_analysis.js +0 -0
  51. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/static/simulator.js +0 -0
  52. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/static/styles.css +0 -0
  53. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/templates/feature_engineering.html +0 -0
  54. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/templates/idea_house.html +0 -0
  55. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/templates/index.html +0 -0
  56. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/templates/inspiration_house.html +0 -0
  57. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/templates/paper_analysis.html +0 -0
  58. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP/templates/simulator.html +0 -0
  59. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/APP//350/277/220/350/241/214/346/211/223/345/274/200/346/210/221.py" +0 -0
  60. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/arXiv_API_Tool_Manual.md +0 -0
  61. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/arxiv_api.py +0 -0
  62. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/forum_functions.py +0 -0
  63. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/sample_mcp_config.json +0 -0
  64. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked/user_config.json +0 -0
  65. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked//347/244/272/344/276/213/345/217/202/350/200/203/346/226/207/346/241/243_BRAIN_Alpha_Test_Requirements_and_Tips.md" +0 -0
  66. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_Alpha_explaination_workflow.md" +0 -0
  67. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_BRAIN_6_Tips_Datafield_Exploration_Guide.md" +0 -0
  68. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_BRAIN_Alpha_Improvement_Workflow.md" +0 -0
  69. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_Dataset_Exploration_Expert_Manual.md" +0 -0
  70. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp.egg-info/dependency_links.txt +0 -0
  71. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp.egg-info/entry_points.txt +0 -0
  72. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp.egg-info/not-zip-safe +0 -0
  73. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp.egg-info/requires.txt +0 -0
  74. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/cnhkmcp.egg-info/top_level.txt +0 -0
  75. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/requirements.txt +0 -0
  76. {cnhkmcp-1.3.7 → cnhkmcp-1.4.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cnhkmcp
3
- Version: 1.3.7
3
+ Version: 1.4.0
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.3.7"
53
+ __version__ = "1.4.0"
54
54
  __author__ = "CNHK"
55
55
  __email__ = "cnhk@example.com"
56
56
 
@@ -1,4 +1,36 @@
1
- # autosimulator.py
1
+ import sys
2
+ import subprocess
3
+ # --- Dependency Check & Auto-Install ---
4
+ required_imports = [
5
+ ("getpass", None),
6
+ ("json", None),
7
+ ("logging", None),
8
+ ("os", None),
9
+ ("threading", None),
10
+ ("time", None),
11
+ ("functools", None),
12
+ ("multiprocessing", None),
13
+ ("pathlib", None),
14
+ ("typing", None),
15
+ ("urllib.parse", None),
16
+ ("pandas", "pandas"),
17
+ ("requests", "requests"),
18
+ ("tqdm", "tqdm"),
19
+ ("pandas.io.formats.style", "pandas"),
20
+ ]
21
+ for mod, pipname in required_imports:
22
+ try:
23
+ if "." in mod:
24
+ __import__(mod.split(".")[0])
25
+ else:
26
+ __import__(mod)
27
+ except ImportError:
28
+ if pipname:
29
+ print(f"Installing missing package: {pipname}")
30
+ subprocess.check_call([sys.executable, "-m", "pip", "install", pipname])
31
+ else:
32
+ print(f"Module {mod} is a built-in or not installable via pip.")
33
+ # --- Script Description ---
2
34
  """
3
35
  Autosimulator for WorldQuant BRAIN platform
4
36
  - Timestamped logger
@@ -1615,10 +1615,9 @@ window.onclick = function(event) {
1615
1615
  // Apply template variables
1616
1616
  function applyTemplate() {
1617
1617
  const variableInput = document.getElementById('variableInput');
1618
- const variables = variableInput.value
1619
- .split(',')
1620
- .map(v => v.trim())
1621
- .filter(v => v !== '');
1618
+
1619
+ // Special handling for bucket() functions to avoid splitting on commas inside them
1620
+ const variables = parseVariablesWithBucketSupport(variableInput.value);
1622
1621
 
1623
1622
  if (variables.length === 0) {
1624
1623
  alert('Please enter at least one variable');
@@ -1646,6 +1645,109 @@ function applyTemplate() {
1646
1645
  errorsDiv.innerHTML = `<div class="success-message">✓ Template <${currentTemplate}/> configured as ${currentConfigType} with ${variables.length} variable${variables.length > 1 ? 's' : ''}</div>`;
1647
1646
  }
1648
1647
 
1648
+ // Parse variables with special support for bucket() functions
1649
+ function parseVariablesWithBucketSupport(input) {
1650
+ const variables = [];
1651
+ let currentVariable = '';
1652
+ let i = 0;
1653
+
1654
+ while (i < input.length) {
1655
+ const char = input[i];
1656
+
1657
+ // Check if we're starting a bucket function
1658
+ if (char === 'b' && i + 7 <= input.length && input.substring(i, i + 7) === 'bucket(') {
1659
+ // Add any previous variable before the bucket function
1660
+ const trimmed = currentVariable.trim();
1661
+ if (trimmed !== '') {
1662
+ variables.push(trimmed);
1663
+ }
1664
+
1665
+ // Find the complete bucket function
1666
+ const bucketFunction = extractBucketFunction(input, i);
1667
+ if (bucketFunction) {
1668
+ // Add the complete bucket function
1669
+ variables.push(bucketFunction.function);
1670
+ currentVariable = '';
1671
+ i = bucketFunction.endIndex + 1; // Move past the bucket function
1672
+ continue;
1673
+ } else {
1674
+ currentVariable += char;
1675
+ }
1676
+ }
1677
+
1678
+ // Regular comma handling
1679
+ if (char === ',') {
1680
+ const trimmed = currentVariable.trim();
1681
+ if (trimmed !== '') {
1682
+ variables.push(trimmed);
1683
+ }
1684
+ currentVariable = '';
1685
+ } else {
1686
+ currentVariable += char;
1687
+ }
1688
+
1689
+ i++;
1690
+ }
1691
+
1692
+ // Add the last variable if there is one
1693
+ const trimmed = currentVariable.trim();
1694
+ if (trimmed !== '') {
1695
+ variables.push(trimmed);
1696
+ }
1697
+
1698
+ return variables;
1699
+ }
1700
+
1701
+ // Extract complete bucket function including all nested content
1702
+ function extractBucketFunction(input, startIndex) {
1703
+ let parenthesesCount = 0;
1704
+ let inQuotes = false;
1705
+ let quoteChar = null;
1706
+ let i = startIndex;
1707
+
1708
+ // Find the start of bucket(
1709
+ if (input.substring(i, i + 7) !== 'bucket(') {
1710
+ return null;
1711
+ }
1712
+
1713
+ parenthesesCount = 1;
1714
+ i += 7; // Move past "bucket("
1715
+
1716
+ while (i < input.length) {
1717
+ const char = input[i];
1718
+
1719
+ // Handle quotes
1720
+ if ((char === '"' || char === "'") && !inQuotes) {
1721
+ inQuotes = true;
1722
+ quoteChar = char;
1723
+ } else if (char === quoteChar && inQuotes) {
1724
+ inQuotes = false;
1725
+ quoteChar = null;
1726
+ }
1727
+
1728
+ // Only count parentheses when not inside quotes
1729
+ if (!inQuotes) {
1730
+ if (char === '(') {
1731
+ parenthesesCount++;
1732
+ } else if (char === ')') {
1733
+ parenthesesCount--;
1734
+ if (parenthesesCount === 0) {
1735
+ // Found the end of bucket function
1736
+ const functionText = input.substring(startIndex, i + 1);
1737
+ return {
1738
+ function: functionText,
1739
+ endIndex: i
1740
+ };
1741
+ }
1742
+ }
1743
+ }
1744
+
1745
+ i++;
1746
+ }
1747
+
1748
+ return null; // No matching closing parenthesis found
1749
+ }
1750
+
1649
1751
  // Clear editor
1650
1752
  function clearEditor() {
1651
1753
  const editor = document.getElementById('expressionEditor');
@@ -13,6 +13,7 @@ from dataclasses import dataclass, asdict
13
13
  from datetime import datetime, timedelta
14
14
  import os
15
15
  import sys
16
+ import math
16
17
  from time import sleep
17
18
 
18
19
  import requests
@@ -371,6 +372,34 @@ class BrainApiClient:
371
372
  except Exception as e:
372
373
  self.log(f"Failed to get alpha details: {str(e)}", "ERROR")
373
374
  raise
375
+
376
+ def _is_atom(self, detail: Optional[Dict[str, Any]]) -> bool:
377
+ """Match atom detection used in extract_regular_alphas.py:
378
+ - Primary signal: 'classifications' entries containing 'SINGLE_DATA_SET'
379
+ - Fallbacks: tags list contains 'atom' or classification id/name contains 'ATOM'
380
+ """
381
+ if not detail or not isinstance(detail, dict):
382
+ return False
383
+
384
+ classifications = detail.get('classifications') or []
385
+ for c in classifications:
386
+ cid = (c.get('id') or c.get('name') or '')
387
+ if isinstance(cid, str) and 'SINGLE_DATA_SET' in cid:
388
+ return True
389
+
390
+ # Fallbacks
391
+ tags = detail.get('tags') or []
392
+ if isinstance(tags, list):
393
+ for t in tags:
394
+ if isinstance(t, str) and t.strip().lower() == 'atom':
395
+ return True
396
+
397
+ for c in classifications:
398
+ cid = (c.get('id') or c.get('name') or '')
399
+ if isinstance(cid, str) and 'ATOM' in cid.upper():
400
+ return True
401
+
402
+ return False
374
403
 
375
404
  async def get_datasets(self, instrument_type: str = "EQUITY", region: str = "USA",
376
405
  delay: int = 1, universe: str = "TOP3000", theme: str = "false", search: Optional[str] = None) -> Dict[str, Any]:
@@ -1165,6 +1194,121 @@ class BrainApiClient:
1165
1194
  self.log(f"Failed to get pyramid multipliers: {str(e)}", "ERROR")
1166
1195
  raise
1167
1196
 
1197
+ async def get_diversity_score(self, start_date: str, end_date: str) -> Dict[str, Any]:
1198
+ """Compute diversity score for regular alphas in a date range.
1199
+
1200
+ Description:
1201
+ This diversity score measures three key aspects of work output: the proportion of works
1202
+ with the "Atom" tag (S_A), atom porportion, the breadth of pyramids covered (S_P), and how evenly works
1203
+ are distributed across those pyramids (S_H). Calculated as their product, it rewards
1204
+ strong performance across all three dimensions—encouraging more Atom-tagged works,
1205
+ wider pyramid coverage, and balanced distribution—with weaknesses in any area lowering
1206
+ the total score significantly.
1207
+
1208
+ Inputs (hints for AI callers):
1209
+ - start_date (str): ISO UTC start datetime, e.g. '2025-08-14T00:00:00Z'
1210
+ - end_date (str): ISO UTC end datetime, e.g. '2025-08-18T23:59:59Z'
1211
+ - Note: this tool always uses 'OS' (submission dates) to define the window; callers do not need to supply a stage.
1212
+ - Note: P_max (total number of possible pyramids) is derived from the platform
1213
+ pyramid-multipliers endpoint and not supplied by callers.
1214
+
1215
+ Returns (compact JSON): {
1216
+ 'diversity_score': float,
1217
+ 'N': int, # total regular alphas in window
1218
+ 'A': int, # number of Atom-tagged works (is_single_data_set)
1219
+ 'P': int, # pyramid coverage count in the sample
1220
+ 'P_max': int, # used max for normalization
1221
+ 'S_A': float, 'S_P': float, 'S_H': float,
1222
+ 'per_pyramid_counts': {pyramid_name: count}
1223
+ }
1224
+ """
1225
+ # Fetch user alphas (always use OS / submission dates per product policy)
1226
+ await self.ensure_authenticated()
1227
+ alphas_resp = await self.get_user_alphas(stage='OS', limit=500, submission_start_date=start_date, submission_end_date=end_date)
1228
+
1229
+ if not isinstance(alphas_resp, dict) or 'results' not in alphas_resp:
1230
+ return {'error': 'Unexpected response from get_user_alphas', 'raw': alphas_resp}
1231
+
1232
+ alphas = alphas_resp['results']
1233
+ regular = [a for a in alphas if a.get('type') == 'REGULAR']
1234
+
1235
+ # Fetch details for each regular alpha
1236
+ pyramid_list = []
1237
+ atom_count = 0
1238
+ per_pyramid = {}
1239
+ for a in regular:
1240
+ try:
1241
+ detail = await self.get_alpha_details(a.get('id'))
1242
+ except Exception:
1243
+ continue
1244
+
1245
+ is_atom = self._is_atom(detail)
1246
+ if is_atom:
1247
+ atom_count += 1
1248
+
1249
+ # Extract pyramids
1250
+ ps = []
1251
+ if isinstance(detail.get('pyramids'), list):
1252
+ ps = [p.get('name') for p in detail.get('pyramids') if p.get('name')]
1253
+ else:
1254
+ pt = detail.get('pyramidThemes') or {}
1255
+ pss = pt.get('pyramids') if isinstance(pt, dict) else None
1256
+ if pss and isinstance(pss, list):
1257
+ ps = [p.get('name') for p in pss if p.get('name')]
1258
+
1259
+ for p in ps:
1260
+ pyramid_list.append(p)
1261
+ per_pyramid[p] = per_pyramid.get(p, 0) + 1
1262
+
1263
+ N = len(regular)
1264
+ A = atom_count
1265
+ P = len(per_pyramid)
1266
+
1267
+ # Determine P_max similarly to the script: use pyramid multipliers if available
1268
+ P_max = None
1269
+ try:
1270
+ pm = await self.get_pyramid_multipliers()
1271
+ if isinstance(pm, dict) and 'pyramids' in pm:
1272
+ pyramids_list = pm.get('pyramids') or []
1273
+ P_max = len(pyramids_list)
1274
+ except Exception:
1275
+ P_max = None
1276
+
1277
+ if not P_max or P_max <= 0:
1278
+ P_max = max(P, 1)
1279
+
1280
+ # Component scores
1281
+ S_A = (A / N) if N > 0 else 0.0
1282
+ S_P = (P / P_max) if P_max > 0 else 0.0
1283
+
1284
+ # Entropy
1285
+ S_H = 0.0
1286
+ if P <= 1 or not per_pyramid:
1287
+ S_H = 0.0
1288
+ else:
1289
+ total_occ = sum(per_pyramid.values())
1290
+ H = 0.0
1291
+ for cnt in per_pyramid.values():
1292
+ q = cnt / total_occ if total_occ > 0 else 0
1293
+ if q > 0:
1294
+ H -= q * math.log2(q)
1295
+ max_H = math.log2(P) if P > 0 else 1
1296
+ S_H = (H / max_H) if max_H > 0 else 0.0
1297
+
1298
+ diversity_score = S_A * S_P * S_H
1299
+
1300
+ return {
1301
+ 'diversity_score': diversity_score,
1302
+ 'N': N,
1303
+ 'A': A,
1304
+ 'P': P,
1305
+ 'P_max': P_max,
1306
+ 'S_A': S_A,
1307
+ 'S_P': S_P,
1308
+ 'S_H': S_H,
1309
+ 'per_pyramid_counts': per_pyramid
1310
+ }
1311
+
1168
1312
  async def get_pyramid_alphas(self, start_date: Optional[str] = None,
1169
1313
  end_date: Optional[str] = None) -> Dict[str, Any]:
1170
1314
  """Get user's current alpha distribution across pyramid categories."""
@@ -1514,6 +1658,25 @@ async def authenticate(email: Optional[str] = "", password: Optional[str] = "")
1514
1658
  except Exception as e:
1515
1659
  return {"error": str(e)}
1516
1660
 
1661
+
1662
+ @mcp.tool()
1663
+ async def get_diversity_score(start_date: str, end_date: str) -> Dict[str, Any]:
1664
+ """Compute and return the diversity score for REGULAR alphas in a submission-date window.
1665
+
1666
+ This MCP tool wraps BrainApiClient.get_diversity_score and always uses submission dates (OS).
1667
+
1668
+ Inputs:
1669
+ - start_date: ISO UTC start datetime (e.g. '2025-08-14T00:00:00Z')
1670
+ - end_date: ISO UTC end datetime (e.g. '2025-08-18T23:59:59Z')
1671
+ - p_max: optional integer total number of pyramid categories for normalization
1672
+
1673
+ Returns: compact JSON with diversity_score, N, A, P, P_max, S_A, S_P, S_H, per_pyramid_counts
1674
+ """
1675
+ try:
1676
+ return await brain_client.get_diversity_score(start_date=start_date, end_date=end_date)
1677
+ except Exception as e:
1678
+ return {"error": str(e)}
1679
+
1517
1680
  @mcp.tool()
1518
1681
  async def manage_config(action: str = "get", settings: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
1519
1682
  """
@@ -2284,7 +2447,7 @@ async def read_specific_documentation(page_id: str) -> Dict[str, Any]:
2284
2447
  # Badge status MCP tool removed as requested
2285
2448
 
2286
2449
  @mcp.tool()
2287
- async def create_multi_regularAlpha_simulation(
2450
+ async def create_multiSim(
2288
2451
  alpha_expressions: List[str],
2289
2452
  instrument_type: str = "EQUITY",
2290
2453
  region: str = "USA",
@@ -2574,10 +2737,10 @@ async def get_daily_and_quarterly_payment(email: str = "", password: str = "") -
2574
2737
 
2575
2738
 
2576
2739
 
2577
- # New MCP tool: get_error_message_fromAlphaLocation
2740
+ # New MCP tool: get_SimError_detail
2578
2741
  from typing import Sequence
2579
2742
  @mcp.tool()
2580
- async def get_error_message_fromAlphaLocation(locations: Sequence[str]) -> dict:
2743
+ async def get_SimError_detail(locations: Sequence[str]) -> dict:
2581
2744
  """
2582
2745
  Fetch and parse error/status from multiple simulation locations (URLs).
2583
2746
  Args:
@@ -2616,6 +2779,6 @@ async def get_error_message_fromAlphaLocation(locations: Sequence[str]) -> dict:
2616
2779
  return {"results": results}
2617
2780
 
2618
2781
  if __name__ == "__main__":
2619
- print("🧠 WorldQuant BRAIN MCP Server Starting...", file=sys.stderr)
2782
+ print("WorldQuant BRAIN MCP Server Starting...", file=sys.stderr)
2620
2783
  mcp.run()
2621
2784
 
@@ -34,9 +34,11 @@
34
34
  2. **社区动态**:从消息中提取社区相关信息,如研究论文或热门话题。
35
35
  3. **排行榜变化**:记录用户位置变化。
36
36
  - 使用工具:`mcp_brain-api_get_leaderboard`(设置 `user_id` 为用户 ID,如 "CQ89422")。
37
+ 4. **多样性分数**:收集用户最近一个季度的多样性分数,提出具体建议。
37
38
  - **使用的 MCP 工具**:
38
39
  - `mcp_brain-api_get_messages`:获取平台公告和社区动态。
39
40
  - `mcp_brain-api_get_leaderboard`:获取用户排行榜统计。
41
+ - `mcp_brain-api_get_diversity_scores`:获取用户多样性分数。
40
42
 
41
43
  ### 3. 比赛参与与进度
42
44
  - **步骤**:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cnhkmcp
3
- Version: 1.3.7
3
+ Version: 1.4.0
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
@@ -70,4 +70,5 @@ cnhkmcp/untracked/APP/templates/idea_house.html
70
70
  cnhkmcp/untracked/APP/templates/index.html
71
71
  cnhkmcp/untracked/APP/templates/inspiration_house.html
72
72
  cnhkmcp/untracked/APP/templates/paper_analysis.html
73
- cnhkmcp/untracked/APP/templates/simulator.html
73
+ cnhkmcp/untracked/APP/templates/simulator.html
74
+ cnhkmcp/untracked/__pycache__/forum_functions.cpython-313.pyc
@@ -13,7 +13,7 @@ def read_requirements():
13
13
 
14
14
  setup(
15
15
  name="cnhkmcp",
16
- version="1.3.7",
16
+ version="1.4.0",
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