cnhkmcp 1.3.8__tar.gz → 1.4.1__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.
- {cnhkmcp-1.3.8/cnhkmcp.egg-info → cnhkmcp-1.4.1}/PKG-INFO +1 -1
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/__init__.py +1 -1
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/static/script.js +106 -4
- cnhkmcp-1.4.1/cnhkmcp/untracked/__pycache__/forum_functions.cpython-313.pyc +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/platform_functions.py +168 -4
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_daily_report_workflow.md +2 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1/cnhkmcp.egg-info}/PKG-INFO +1 -1
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp.egg-info/SOURCES.txt +2 -1
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/setup.py +1 -1
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/LICENSE +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/MANIFEST.in +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/README.md +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/.gitignore +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/MODULAR_STRUCTURE.md +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/README.md +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/__pycache__/app.cpython-313.pyc +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/blueprints/__init__.py +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/blueprints/__pycache__/__init__.cpython-313.pyc +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/blueprints/__pycache__/feature_engineering.cpython-313.pyc +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/blueprints/__pycache__/idea_house.cpython-313.pyc +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/blueprints/__pycache__/inspiration_house.cpython-313.pyc +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/blueprints/__pycache__/paper_analysis.cpython-313.pyc +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/blueprints/__pycache__/simulator.cpython-313.pyc +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/blueprints/__pycache__/unified_tools.cpython-313.pyc +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/blueprints/__pycache__/wqb_simulator.cpython-313.pyc +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/blueprints/feature_engineering.py +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/blueprints/idea_house.py +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/blueprints/inspiration_house.py +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/blueprints/paper_analysis.py +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/custom_templates/templates.json +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/hkSimulator/ace_lib.py +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/hkSimulator/autosimulator.py +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/hkSimulator/helpful_functions.py +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/mirror_config.txt +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/operaters.csv +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/requirements.txt +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/run_app.bat +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/run_app.sh +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/setup_tsinghua.bat +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/setup_tsinghua.sh +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/simulator/__pycache__/simulator_wqb.cpython-313.pyc +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/simulator/alpha_submitter.py +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/simulator/simulator_wqb.py +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/ssrn-3332513.pdf +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/static/brain.js +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/static/decoder.js +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/static/feature_engineering.js +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/static/idea_house.js +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/static/inspiration_house.js +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/static/paper_analysis.js +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/static/simulator.js +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/static/styles.css +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/templates/feature_engineering.html +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/templates/idea_house.html +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/templates/index.html +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/templates/inspiration_house.html +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/templates/paper_analysis.html +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP/templates/simulator.html +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/APP//350/277/220/350/241/214/346/211/223/345/274/200/346/210/221.py" +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/arXiv_API_Tool_Manual.md +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/arxiv_api.py +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/forum_functions.py +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/sample_mcp_config.json +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked/user_config.json +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/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
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_Alpha_explaination_workflow.md" +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/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
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_BRAIN_Alpha_Improvement_Workflow.md" +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp/untracked//347/244/272/344/276/213/345/267/245/344/275/234/346/265/201_Dataset_Exploration_Expert_Manual.md" +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp.egg-info/dependency_links.txt +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp.egg-info/entry_points.txt +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp.egg-info/not-zip-safe +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp.egg-info/requires.txt +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/cnhkmcp.egg-info/top_level.txt +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/requirements.txt +0 -0
- {cnhkmcp-1.3.8 → cnhkmcp-1.4.1}/setup.cfg +0 -0
|
@@ -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
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
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,122 @@ class BrainApiClient:
|
|
|
1165
1194
|
self.log(f"Failed to get pyramid multipliers: {str(e)}", "ERROR")
|
|
1166
1195
|
raise
|
|
1167
1196
|
|
|
1197
|
+
async def value_factor_trendScore(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 function calculate the diversity of the users' submission, by checking the diversity, we can have a good understanding on the valuefactor's trend.
|
|
1202
|
+
value factor of a user is defiend by This diversity score, which measures three key aspects of work output: the proportion of works
|
|
1203
|
+
with the "Atom" tag (S_A), atom proportion, the breadth of pyramids covered (S_P), and how evenly works
|
|
1204
|
+
are distributed across those pyramids (S_H). Calculated as their product, it rewards
|
|
1205
|
+
strong performance across all three dimensions—encouraging more Atom-tagged works,
|
|
1206
|
+
wider pyramid coverage, and balanced distribution—with weaknesses in any area lowering
|
|
1207
|
+
the total score significantly.
|
|
1208
|
+
|
|
1209
|
+
Inputs (hints for AI callers):
|
|
1210
|
+
- start_date (str): ISO UTC start datetime, e.g. '2025-08-14T00:00:00Z'
|
|
1211
|
+
- end_date (str): ISO UTC end datetime, e.g. '2025-08-18T23:59:59Z'
|
|
1212
|
+
- Note: this tool always uses 'OS' (submission dates) to define the window; callers do not need to supply a stage.
|
|
1213
|
+
- Note: P_max (total number of possible pyramids) is derived from the platform
|
|
1214
|
+
pyramid-multipliers endpoint and not supplied by callers.
|
|
1215
|
+
|
|
1216
|
+
Returns (compact JSON): {
|
|
1217
|
+
'diversity_score': float,
|
|
1218
|
+
'N': int, # total regular alphas in window
|
|
1219
|
+
'A': int, # number of Atom-tagged works (is_single_data_set)
|
|
1220
|
+
'P': int, # pyramid coverage count in the sample
|
|
1221
|
+
'P_max': int, # used max for normalization
|
|
1222
|
+
'S_A': float, 'S_P': float, 'S_H': float,
|
|
1223
|
+
'per_pyramid_counts': {pyramid_name: count}
|
|
1224
|
+
}
|
|
1225
|
+
"""
|
|
1226
|
+
# Fetch user alphas (always use OS / submission dates per product policy)
|
|
1227
|
+
await self.ensure_authenticated()
|
|
1228
|
+
alphas_resp = await self.get_user_alphas(stage='OS', limit=500, submission_start_date=start_date, submission_end_date=end_date)
|
|
1229
|
+
|
|
1230
|
+
if not isinstance(alphas_resp, dict) or 'results' not in alphas_resp:
|
|
1231
|
+
return {'error': 'Unexpected response from get_user_alphas', 'raw': alphas_resp}
|
|
1232
|
+
|
|
1233
|
+
alphas = alphas_resp['results']
|
|
1234
|
+
regular = [a for a in alphas if a.get('type') == 'REGULAR']
|
|
1235
|
+
|
|
1236
|
+
# Fetch details for each regular alpha
|
|
1237
|
+
pyramid_list = []
|
|
1238
|
+
atom_count = 0
|
|
1239
|
+
per_pyramid = {}
|
|
1240
|
+
for a in regular:
|
|
1241
|
+
try:
|
|
1242
|
+
detail = await self.get_alpha_details(a.get('id'))
|
|
1243
|
+
except Exception:
|
|
1244
|
+
continue
|
|
1245
|
+
|
|
1246
|
+
is_atom = self._is_atom(detail)
|
|
1247
|
+
if is_atom:
|
|
1248
|
+
atom_count += 1
|
|
1249
|
+
|
|
1250
|
+
# Extract pyramids
|
|
1251
|
+
ps = []
|
|
1252
|
+
if isinstance(detail.get('pyramids'), list):
|
|
1253
|
+
ps = [p.get('name') for p in detail.get('pyramids') if p.get('name')]
|
|
1254
|
+
else:
|
|
1255
|
+
pt = detail.get('pyramidThemes') or {}
|
|
1256
|
+
pss = pt.get('pyramids') if isinstance(pt, dict) else None
|
|
1257
|
+
if pss and isinstance(pss, list):
|
|
1258
|
+
ps = [p.get('name') for p in pss if p.get('name')]
|
|
1259
|
+
|
|
1260
|
+
for p in ps:
|
|
1261
|
+
pyramid_list.append(p)
|
|
1262
|
+
per_pyramid[p] = per_pyramid.get(p, 0) + 1
|
|
1263
|
+
|
|
1264
|
+
N = len(regular)
|
|
1265
|
+
A = atom_count
|
|
1266
|
+
P = len(per_pyramid)
|
|
1267
|
+
|
|
1268
|
+
# Determine P_max similarly to the script: use pyramid multipliers if available
|
|
1269
|
+
P_max = None
|
|
1270
|
+
try:
|
|
1271
|
+
pm = await self.get_pyramid_multipliers()
|
|
1272
|
+
if isinstance(pm, dict) and 'pyramids' in pm:
|
|
1273
|
+
pyramids_list = pm.get('pyramids') or []
|
|
1274
|
+
P_max = len(pyramids_list)
|
|
1275
|
+
except Exception:
|
|
1276
|
+
P_max = None
|
|
1277
|
+
|
|
1278
|
+
if not P_max or P_max <= 0:
|
|
1279
|
+
P_max = max(P, 1)
|
|
1280
|
+
|
|
1281
|
+
# Component scores
|
|
1282
|
+
S_A = (A / N) if N > 0 else 0.0
|
|
1283
|
+
S_P = (P / P_max) if P_max > 0 else 0.0
|
|
1284
|
+
|
|
1285
|
+
# Entropy
|
|
1286
|
+
S_H = 0.0
|
|
1287
|
+
if P <= 1 or not per_pyramid:
|
|
1288
|
+
S_H = 0.0
|
|
1289
|
+
else:
|
|
1290
|
+
total_occ = sum(per_pyramid.values())
|
|
1291
|
+
H = 0.0
|
|
1292
|
+
for cnt in per_pyramid.values():
|
|
1293
|
+
q = cnt / total_occ if total_occ > 0 else 0
|
|
1294
|
+
if q > 0:
|
|
1295
|
+
H -= q * math.log2(q)
|
|
1296
|
+
max_H = math.log2(P) if P > 0 else 1
|
|
1297
|
+
S_H = (H / max_H) if max_H > 0 else 0.0
|
|
1298
|
+
|
|
1299
|
+
diversity_score = S_A * S_P * S_H
|
|
1300
|
+
|
|
1301
|
+
return {
|
|
1302
|
+
'diversity_score': diversity_score,
|
|
1303
|
+
'N': N,
|
|
1304
|
+
'A': A,
|
|
1305
|
+
'P': P,
|
|
1306
|
+
'P_max': P_max,
|
|
1307
|
+
'S_A': S_A,
|
|
1308
|
+
'S_P': S_P,
|
|
1309
|
+
'S_H': S_H,
|
|
1310
|
+
'per_pyramid_counts': per_pyramid
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1168
1313
|
async def get_pyramid_alphas(self, start_date: Optional[str] = None,
|
|
1169
1314
|
end_date: Optional[str] = None) -> Dict[str, Any]:
|
|
1170
1315
|
"""Get user's current alpha distribution across pyramid categories."""
|
|
@@ -1514,6 +1659,25 @@ async def authenticate(email: Optional[str] = "", password: Optional[str] = "")
|
|
|
1514
1659
|
except Exception as e:
|
|
1515
1660
|
return {"error": str(e)}
|
|
1516
1661
|
|
|
1662
|
+
|
|
1663
|
+
@mcp.tool()
|
|
1664
|
+
async def value_factor_trendScore(start_date: str, end_date: str) -> Dict[str, Any]:
|
|
1665
|
+
"""Compute and return the diversity score for REGULAR alphas in a submission-date window.
|
|
1666
|
+
This function calculate the diversity of the users' submission, by checking the diversity, we can have a good understanding on the valuefactor's trend.
|
|
1667
|
+
This MCP tool wraps BrainApiClient.value_factor_trendScore and always uses submission dates (OS).
|
|
1668
|
+
|
|
1669
|
+
Inputs:
|
|
1670
|
+
- start_date: ISO UTC start datetime (e.g. '2025-08-14T00:00:00Z')
|
|
1671
|
+
- end_date: ISO UTC end datetime (e.g. '2025-08-18T23:59:59Z')
|
|
1672
|
+
- p_max: optional integer total number of pyramid categories for normalization
|
|
1673
|
+
|
|
1674
|
+
Returns: compact JSON with diversity_score, N, A, P, P_max, S_A, S_P, S_H, per_pyramid_counts
|
|
1675
|
+
"""
|
|
1676
|
+
try:
|
|
1677
|
+
return await brain_client.value_factor_trendScore(start_date=start_date, end_date=end_date)
|
|
1678
|
+
except Exception as e:
|
|
1679
|
+
return {"error": str(e)}
|
|
1680
|
+
|
|
1517
1681
|
@mcp.tool()
|
|
1518
1682
|
async def manage_config(action: str = "get", settings: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
1519
1683
|
"""
|
|
@@ -2284,7 +2448,7 @@ async def read_specific_documentation(page_id: str) -> Dict[str, Any]:
|
|
|
2284
2448
|
# Badge status MCP tool removed as requested
|
|
2285
2449
|
|
|
2286
2450
|
@mcp.tool()
|
|
2287
|
-
async def
|
|
2451
|
+
async def create_multiSim(
|
|
2288
2452
|
alpha_expressions: List[str],
|
|
2289
2453
|
instrument_type: str = "EQUITY",
|
|
2290
2454
|
region: str = "USA",
|
|
@@ -2574,10 +2738,10 @@ async def get_daily_and_quarterly_payment(email: str = "", password: str = "") -
|
|
|
2574
2738
|
|
|
2575
2739
|
|
|
2576
2740
|
|
|
2577
|
-
# New MCP tool:
|
|
2741
|
+
# New MCP tool: get_SimError_detail
|
|
2578
2742
|
from typing import Sequence
|
|
2579
2743
|
@mcp.tool()
|
|
2580
|
-
async def
|
|
2744
|
+
async def get_SimError_detail(locations: Sequence[str]) -> dict:
|
|
2581
2745
|
"""
|
|
2582
2746
|
Fetch and parse error/status from multiple simulation locations (URLs).
|
|
2583
2747
|
Args:
|
|
@@ -2616,6 +2780,6 @@ async def get_error_message_fromAlphaLocation(locations: Sequence[str]) -> dict:
|
|
|
2616
2780
|
return {"results": results}
|
|
2617
2781
|
|
|
2618
2782
|
if __name__ == "__main__":
|
|
2619
|
-
print("
|
|
2783
|
+
print("WorldQuant BRAIN MCP Server Starting...", file=sys.stderr)
|
|
2620
2784
|
mcp.run()
|
|
2621
2785
|
|
|
@@ -34,9 +34,11 @@
|
|
|
34
34
|
2. **社区动态**:从消息中提取社区相关信息,如研究论文或热门话题。
|
|
35
35
|
3. **排行榜变化**:记录用户位置变化。
|
|
36
36
|
- 使用工具:`mcp_brain-api_get_leaderboard`(设置 `user_id` 为用户 ID,如 "CQ89422")。
|
|
37
|
+
4. **多样性分数**:收集用户最近一个季度的多样性分数,获知其value factor趋势,该分数捕捉用户提交Alpha的多样性,来判断其value factor的变化趋势,在0-1之间,越高越好,据此提出具体建议。
|
|
37
38
|
- **使用的 MCP 工具**:
|
|
38
39
|
- `mcp_brain-api_get_messages`:获取平台公告和社区动态。
|
|
39
40
|
- `mcp_brain-api_get_leaderboard`:获取用户排行榜统计。
|
|
41
|
+
- `mcp_brain-api_value_factor_trendScore`:用户value factor趋势,又名多样性分数。
|
|
40
42
|
|
|
41
43
|
### 3. 比赛参与与进度
|
|
42
44
|
- **步骤**:
|
|
@@ -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.
|
|
16
|
+
version="1.4.1",
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|