cnhkmcp 1.1.5__py3-none-any.whl → 1.1.7__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.
cnhkmcp/__init__.py CHANGED
@@ -1,125 +1,125 @@
1
- """
2
- CNHK MCP Server - A comprehensive Model Context Protocol server for quantitative trading platform integration.
3
- """
4
-
5
- from .untracked.platform_functions import (
6
- BrainApiClient,
7
- authenticate,
8
- create_simulation,
9
- get_simulation_status,
10
- wait_for_simulation,
11
- get_alpha_details,
12
- get_datasets,
13
- get_datafields,
14
- get_alpha_pnl,
15
- get_user_alphas,
16
- submit_alpha,
17
- get_events,
18
- get_leaderboard,
19
- batch_process_alphas,
20
- analyze_alpha_performance,
21
- get_operators,
22
- run_selection,
23
- get_user_profile,
24
- get_tutorials,
25
- get_messages_summary,
26
- get_messages,
27
- get_alpha_record_sets,
28
- check_production_correlation,
29
- check_self_correlation,
30
- get_submission_check,
31
- set_alpha_properties,
32
- get_user_activities,
33
- get_pyramid_multipliers,
34
- get_pyramid_alphas,
35
- get_user_competitions,
36
- get_competition_details,
37
- get_competition_agreement,
38
- get_instrument_options,
39
- performance_comparison,
40
- combine_test_results,
41
- generate_alpha_links,
42
- get_tutorial_page,
43
- get_tutorial_badge_status
44
- )
45
-
46
- from .untracked.forum_functions import (
47
- ForumClient,
48
- get_glossary_terms,
49
- search_forum_posts,
50
- read_full_forum_post
51
- )
52
-
53
- __version__ = "1.1.5"
54
- __author__ = "CNHK"
55
- __email__ = "cnhk@example.com"
56
-
57
- __all__ = [
58
- # Main API client
59
- "BrainApiClient",
60
-
61
- # Authentication
62
- "authenticate",
63
-
64
- # Simulation management
65
- "create_simulation",
66
- "get_simulation_status",
67
- "wait_for_simulation",
68
-
69
- # Alpha management
70
- "get_alpha_details",
71
- "submit_alpha",
72
- "get_user_alphas",
73
- "get_alpha_pnl",
74
- "get_alpha_record_sets",
75
- "set_alpha_properties",
76
- "check_production_correlation",
77
- "check_self_correlation",
78
- "get_submission_check",
79
-
80
- # Data access
81
- "get_datasets",
82
- "get_datafields",
83
- "get_operators",
84
- "run_selection",
85
- "get_instrument_options",
86
-
87
- # Performance analysis
88
- "analyze_alpha_performance",
89
- "performance_comparison",
90
- "combine_test_results",
91
-
92
- # User management
93
- "get_user_profile",
94
- "get_user_activities",
95
- "get_user_competitions",
96
-
97
- # Competition and events
98
- "get_events",
99
- "get_leaderboard",
100
- "get_competition_details",
101
- "get_competition_agreement",
102
-
103
- # Pyramid system
104
- "get_pyramid_multipliers",
105
- "get_pyramid_alphas",
106
-
107
- # Tutorials and documentation
108
- "get_tutorials",
109
- "get_tutorial_page",
110
- "get_tutorial_badge_status",
111
-
112
- # Messages
113
- "get_messages_summary",
114
- "get_messages",
115
-
116
- # Utilities
117
- "batch_process_alphas",
118
- "generate_alpha_links",
119
-
120
- # Forum functionality
121
- "ForumClient",
122
- "get_glossary_terms",
123
- "search_forum_posts",
124
- "read_full_forum_post"
1
+ """
2
+ CNHK MCP Server - A comprehensive Model Context Protocol server for quantitative trading platform integration.
3
+ """
4
+
5
+ from .untracked.platform_functions import (
6
+ BrainApiClient,
7
+ authenticate,
8
+ create_simulation,
9
+ get_simulation_status,
10
+ wait_for_simulation,
11
+ get_alpha_details,
12
+ get_datasets,
13
+ get_datafields,
14
+ get_alpha_pnl,
15
+ get_user_alphas,
16
+ submit_alpha,
17
+ get_events,
18
+ get_leaderboard,
19
+ batch_process_alphas,
20
+ analyze_alpha_performance,
21
+ get_operators,
22
+ run_selection,
23
+ get_user_profile,
24
+ get_tutorials,
25
+ get_messages_summary,
26
+ get_messages,
27
+ get_alpha_record_sets,
28
+ check_production_correlation,
29
+ check_self_correlation,
30
+ get_submission_check,
31
+ set_alpha_properties,
32
+ get_user_activities,
33
+ get_pyramid_multipliers,
34
+ get_pyramid_alphas,
35
+ get_user_competitions,
36
+ get_competition_details,
37
+ get_competition_agreement,
38
+ get_instrument_options,
39
+ performance_comparison,
40
+ combine_test_results,
41
+ generate_alpha_links,
42
+ get_tutorial_page,
43
+ get_tutorial_badge_status
44
+ )
45
+
46
+ from .untracked.forum_functions import (
47
+ ForumClient,
48
+ get_glossary_terms,
49
+ search_forum_posts,
50
+ read_full_forum_post
51
+ )
52
+
53
+ __version__ = "1.1.7"
54
+ __author__ = "CNHK"
55
+ __email__ = "cnhk@example.com"
56
+
57
+ __all__ = [
58
+ # Main API client
59
+ "BrainApiClient",
60
+
61
+ # Authentication
62
+ "authenticate",
63
+
64
+ # Simulation management
65
+ "create_simulation",
66
+ "get_simulation_status",
67
+ "wait_for_simulation",
68
+
69
+ # Alpha management
70
+ "get_alpha_details",
71
+ "submit_alpha",
72
+ "get_user_alphas",
73
+ "get_alpha_pnl",
74
+ "get_alpha_record_sets",
75
+ "set_alpha_properties",
76
+ "check_production_correlation",
77
+ "check_self_correlation",
78
+ "get_submission_check",
79
+
80
+ # Data access
81
+ "get_datasets",
82
+ "get_datafields",
83
+ "get_operators",
84
+ "run_selection",
85
+ "get_instrument_options",
86
+
87
+ # Performance analysis
88
+ "analyze_alpha_performance",
89
+ "performance_comparison",
90
+ "combine_test_results",
91
+
92
+ # User management
93
+ "get_user_profile",
94
+ "get_user_activities",
95
+ "get_user_competitions",
96
+
97
+ # Competition and events
98
+ "get_events",
99
+ "get_leaderboard",
100
+ "get_competition_details",
101
+ "get_competition_agreement",
102
+
103
+ # Pyramid system
104
+ "get_pyramid_multipliers",
105
+ "get_pyramid_alphas",
106
+
107
+ # Tutorials and documentation
108
+ "get_tutorials",
109
+ "get_tutorial_page",
110
+ "get_tutorial_badge_status",
111
+
112
+ # Messages
113
+ "get_messages_summary",
114
+ "get_messages",
115
+
116
+ # Utilities
117
+ "batch_process_alphas",
118
+ "generate_alpha_links",
119
+
120
+ # Forum functionality
121
+ "ForumClient",
122
+ "get_glossary_terms",
123
+ "search_forum_posts",
124
+ "read_full_forum_post"
125
125
  ]
@@ -81,8 +81,8 @@ class BrainApiClient:
81
81
  print(f"[{level}] {message}", file=sys.stderr)
82
82
 
83
83
  async def authenticate(self, email: str, password: str) -> Dict[str, Any]:
84
- """Authenticate with WorldQuant BRAIN platform using Basic Authentication."""
85
- self.log("🔐 Starting Basic Authentication process...", "INFO")
84
+ """Authenticate with WorldQuant BRAIN platform with biometric support."""
85
+ self.log("🔐 Starting Authentication process...", "INFO")
86
86
 
87
87
  try:
88
88
  # Store credentials for potential re-authentication
@@ -106,7 +106,7 @@ class BrainApiClient:
106
106
 
107
107
  # Check for successful authentication (status code 201)
108
108
  if response.status_code == 201:
109
- self.log("✅ Basic Authentication successful", "SUCCESS")
109
+ self.log("✅ Authentication successful", "SUCCESS")
110
110
 
111
111
  # Check if JWT token was automatically stored by session
112
112
  jwt_token = self.session.cookies.get('t')
@@ -120,10 +120,26 @@ class BrainApiClient:
120
120
  'user': {'email': email},
121
121
  'status': 'authenticated',
122
122
  'permissions': ['read', 'write'],
123
- 'message': 'Basic Authentication successful',
123
+ 'message': 'Authentication successful',
124
124
  'status_code': response.status_code,
125
125
  'has_jwt': jwt_token is not None
126
126
  }
127
+
128
+ # Check if biometric authentication is required (401 with persona)
129
+ elif response.status_code == 401:
130
+ www_auth = response.headers.get("WWW-Authenticate")
131
+ location = response.headers.get("Location")
132
+
133
+ if www_auth == "persona" and location:
134
+ self.log("🔴 Biometric authentication required", "INFO")
135
+
136
+ # Handle biometric authentication
137
+ from urllib.parse import urljoin
138
+ biometric_url = urljoin(response.url, location)
139
+
140
+ return await self._handle_biometric_auth(biometric_url, email)
141
+ else:
142
+ raise Exception("Incorrect email or password")
127
143
  else:
128
144
  raise Exception(f"Authentication failed with status code: {response.status_code}")
129
145
 
@@ -134,6 +150,97 @@ class BrainApiClient:
134
150
  self.log(f"❌ Authentication failed: {str(e)}", "ERROR")
135
151
  raise
136
152
 
153
+ async def _handle_biometric_auth(self, biometric_url: str, email: str) -> Dict[str, Any]:
154
+ """Handle biometric authentication using browser automation."""
155
+ self.log("🌐 Starting biometric authentication...", "INFO")
156
+
157
+ try:
158
+ # Import selenium for browser automation
159
+ from selenium import webdriver
160
+ from selenium.webdriver.chrome.options import Options
161
+ import time
162
+
163
+ # Setup Chrome options
164
+ options = Options()
165
+ options.add_argument('--no-sandbox')
166
+ options.add_argument('--disable-dev-shm-usage')
167
+
168
+ driver = None
169
+ try:
170
+ # Open browser with timeout
171
+ driver = webdriver.Chrome(options=options)
172
+ # Set a short timeout so it doesn't wait forever
173
+ driver.set_page_load_timeout(80) # Only wait 5 seconds
174
+
175
+ self.log("🌐 Opening browser for biometric authentication...", "INFO")
176
+
177
+ # Try to open the URL but handle timeout
178
+ try:
179
+ driver.get(biometric_url)
180
+ self.log("✅ Browser page loaded successfully", "SUCCESS")
181
+ except Exception as timeout_error:
182
+ self.log(f"⚠️ Page load timeout (expected): {str(timeout_error)[:50]}...", "WARNING")
183
+ self.log("✅ Browser window is open for biometric authentication", "INFO")
184
+
185
+ # Print instructions
186
+ print("\n" + "="*60, file=sys.stderr)
187
+ print("🔒 BIOMETRIC AUTHENTICATION REQUIRED", file=sys.stderr)
188
+ print("="*60, file=sys.stderr)
189
+ print("🌐 Browser window is open with biometric authentication page", file=sys.stderr)
190
+ print("🔧 Complete the biometric authentication in the browser", file=sys.stderr)
191
+ print("⏳ The system will automatically check when you're done...", file=sys.stderr)
192
+ print("="*60, file=sys.stderr)
193
+
194
+ # Keep checking until authentication is complete
195
+ max_attempts = 60 # 5 minutes maximum (60 * 5 seconds)
196
+ attempt = 0
197
+
198
+ while attempt < max_attempts:
199
+ time.sleep(5) # Check every 5 seconds
200
+ attempt += 1
201
+
202
+ # Check if authentication completed
203
+ check_response = self.session.post(biometric_url)
204
+ self.log(f"🔄 Checking authentication status (attempt {attempt}/{max_attempts}): {check_response.status_code}", "INFO")
205
+
206
+ if check_response.status_code == 201:
207
+ self.log("✅ Biometric authentication successful!", "SUCCESS")
208
+
209
+ # Close browser
210
+ driver.quit()
211
+
212
+ # Check JWT token
213
+ jwt_token = self.session.cookies.get('t')
214
+ if jwt_token:
215
+ self.log("✅ JWT token received", "SUCCESS")
216
+
217
+ # Return success response
218
+ return {
219
+ 'user': {'email': email},
220
+ 'status': 'authenticated',
221
+ 'permissions': ['read', 'write'],
222
+ 'message': 'Biometric authentication successful',
223
+ 'status_code': check_response.status_code,
224
+ 'has_jwt': jwt_token is not None
225
+ }
226
+
227
+ # If we get here, authentication timed out
228
+ if driver:
229
+ driver.quit()
230
+ raise Exception("Biometric authentication timed out")
231
+
232
+ except Exception as driver_error:
233
+ if driver:
234
+ try:
235
+ driver.quit()
236
+ except:
237
+ pass
238
+ raise Exception(f"Browser automation error: {driver_error}")
239
+
240
+ except Exception as e:
241
+ self.log(f"❌ Biometric authentication failed: {str(e)}", "ERROR")
242
+ raise
243
+
137
244
  async def is_authenticated(self) -> bool:
138
245
  """Check if currently authenticated using JWT token."""
139
246
  try:
@@ -263,7 +370,7 @@ class BrainApiClient:
263
370
  raise
264
371
 
265
372
  async def get_datasets(self, instrument_type: str = "EQUITY", region: str = "USA",
266
- delay: int = 1, universe: str = "TOP3000", theme: str = "false") -> Dict[str, Any]:
373
+ delay: int = 1, universe: str = "TOP3000", theme: str = "false", search: Optional[str] = None) -> Dict[str, Any]:
267
374
  """Get available datasets."""
268
375
  await self.ensure_authenticated()
269
376
 
@@ -276,9 +383,14 @@ class BrainApiClient:
276
383
  'theme': theme
277
384
  }
278
385
 
386
+ if search:
387
+ params['search'] = search
388
+
279
389
  response = self.session.get(f"{self.base_url}/data-sets", params=params)
280
390
  response.raise_for_status()
281
- return response.json()
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"
393
+ return response
282
394
  except Exception as e:
283
395
  self.log(f"Failed to get datasets: {str(e)}", "ERROR")
284
396
  raise
@@ -310,7 +422,9 @@ class BrainApiClient:
310
422
 
311
423
  response = self.session.get(f"{self.base_url}/data-fields", params=params)
312
424
  response.raise_for_status()
313
- return response.json()
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"
427
+ return response
314
428
  except Exception as e:
315
429
  self.log(f"Failed to get datafields: {str(e)}", "ERROR")
316
430
  raise
@@ -827,26 +941,43 @@ class BrainApiClient:
827
941
  self.log(f"Failed to get pyramid multipliers: {str(e)}", "ERROR")
828
942
  raise
829
943
 
830
- async def get_pyramid_alphas(self, region: Optional[str] = None, delay: Optional[int] = None,
831
- category: Optional[str] = None, start_date: Optional[str] = None,
944
+ async def get_pyramid_alphas(self, start_date: Optional[str] = None,
832
945
  end_date: Optional[str] = None) -> Dict[str, Any]:
833
946
  """Get user's current alpha distribution across pyramid categories."""
834
947
  await self.ensure_authenticated()
835
948
 
836
949
  try:
837
950
  params = {}
838
- if region:
839
- params['region'] = region
840
- if delay is not None:
841
- params['delay'] = delay
842
- if category:
843
- params['category'] = category
844
951
  if start_date:
845
952
  params['startDate'] = start_date
846
953
  if end_date:
847
954
  params['endDate'] = end_date
848
955
 
849
- response = self.session.get(f"{self.base_url}/pyramid/alphas", params=params)
956
+ # Try the user-specific activities endpoint first (like pyramid-multipliers)
957
+ response = self.session.get(f"{self.base_url}/users/self/activities/pyramid-alphas", params=params)
958
+
959
+ # If that fails, try alternative endpoints
960
+ if response.status_code == 404:
961
+ # Try alternative endpoint structure
962
+ response = self.session.get(f"{self.base_url}/users/self/pyramid/alphas", params=params)
963
+
964
+ if response.status_code == 404:
965
+ # Try yet another alternative
966
+ response = self.session.get(f"{self.base_url}/activities/pyramid-alphas", params=params)
967
+
968
+ if response.status_code == 404:
969
+ # Return an informative error with what we tried
970
+ return {
971
+ "error": "Pyramid alphas endpoint not found",
972
+ "tried_endpoints": [
973
+ "/users/self/activities/pyramid-alphas",
974
+ "/users/self/pyramid/alphas",
975
+ "/activities/pyramid-alphas",
976
+ "/pyramid/alphas"
977
+ ],
978
+ "suggestion": "This endpoint may not be available in the current API version"
979
+ }
980
+
850
981
  response.raise_for_status()
851
982
  return response.json()
852
983
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cnhkmcp
3
- Version: 1.1.5
3
+ Version: 1.1.7
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
@@ -0,0 +1,10 @@
1
+ cnhkmcp/__init__.py,sha256=zA4Je1BkR8_9NorDUU5n_QEozwqBquxVGsUrGk3yHjc,2758
2
+ cnhkmcp/untracked/forum_functions.py,sha256=78wzvN_UYWwbWU40q8_FJNSFPJnND6W9ZRey6MSSiEk,45516
3
+ cnhkmcp/untracked/platform_functions.py,sha256=sDW-tOhWUT41YuO-A3ugimkrNEj0ShMEUYp4EFVQpeA,77441
4
+ cnhkmcp/untracked/user_config.json,sha256=_INn1X1qIsITrmEno-BRlQOAGm9wnNCw-6B333DEvnk,695
5
+ cnhkmcp-1.1.7.dist-info/licenses/LICENSE,sha256=QLxO2eNMnJQEdI_R1UV2AOD-IvuA8zVrkHWA4D9gtoc,1081
6
+ cnhkmcp-1.1.7.dist-info/METADATA,sha256=sNnIQ4NvGvk6qScb5HBmfhUiP9-OiDG1hsGQLn-RazA,5171
7
+ cnhkmcp-1.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ cnhkmcp-1.1.7.dist-info/entry_points.txt,sha256=lTQieVyIvjhSMK4fT-XwnccY-JBC1H4vVQ3V9dDM-Pc,70
9
+ cnhkmcp-1.1.7.dist-info/top_level.txt,sha256=x--ibUcSgOS9Z_RWK2Qc-vfs7DaXQN-WMaaxEETJ1Bw,8
10
+ cnhkmcp-1.1.7.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- cnhkmcp/__init__.py,sha256=tguzH5AFA9YrG97csGDkzjyEixyN4p5H3-lIlQ58uCY,2882
2
- cnhkmcp/untracked/forum_functions.py,sha256=78wzvN_UYWwbWU40q8_FJNSFPJnND6W9ZRey6MSSiEk,45516
3
- cnhkmcp/untracked/platform_functions.py,sha256=LuZt6GidH8Qy27DtcUgBCgED3iZf5WF-Hq_3xm-HOjM,70748
4
- cnhkmcp/untracked/user_config.json,sha256=_INn1X1qIsITrmEno-BRlQOAGm9wnNCw-6B333DEvnk,695
5
- cnhkmcp-1.1.5.dist-info/licenses/LICENSE,sha256=QLxO2eNMnJQEdI_R1UV2AOD-IvuA8zVrkHWA4D9gtoc,1081
6
- cnhkmcp-1.1.5.dist-info/METADATA,sha256=_bTEFa_BKyb2lbedU-JNhCU6oKsx1bH2xXmHgnBziKI,5171
7
- cnhkmcp-1.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
- cnhkmcp-1.1.5.dist-info/entry_points.txt,sha256=lTQieVyIvjhSMK4fT-XwnccY-JBC1H4vVQ3V9dDM-Pc,70
9
- cnhkmcp-1.1.5.dist-info/top_level.txt,sha256=x--ibUcSgOS9Z_RWK2Qc-vfs7DaXQN-WMaaxEETJ1Bw,8
10
- cnhkmcp-1.1.5.dist-info/RECORD,,